This commit is contained in:
Frank 2020-07-12 20:57:37 +02:00
parent 96f2ad06e2
commit b67750d808
2 changed files with 742 additions and 13 deletions

View File

@ -37,7 +37,7 @@
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- ### Contributions: -- ### Contributions: **funkyfranky**
-- --
-- === -- ===
-- --
@ -48,9 +48,10 @@
do -- SET_BASE do -- SET_BASE
--- @type SET_BASE --- @type SET_BASE
-- @field #table Filter -- @field #table Filter Table of filters.
-- @field #table Set -- @field #table Set Table of objects.
-- @field #table List -- @field #table Index Table of indicies.
-- @field #table List Unused table.
-- @field Core.Scheduler#SCHEDULER CallScheduler -- @field Core.Scheduler#SCHEDULER CallScheduler
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@ -77,6 +78,10 @@ do -- SET_BASE
Set = {}, Set = {},
List = {}, List = {},
Index = {}, Index = {},
Database = nil,
CallScheduler=nil,
TimeInterval=nil,
YieldInterval=nil,
} }
@ -224,8 +229,8 @@ do -- SET_BASE
--- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using a given ObjectName as the index. --- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using a given ObjectName as the index.
-- @param #SET_BASE self -- @param #SET_BASE self
-- @param #string ObjectName -- @param #string ObjectName The name of the object.
-- @param Core.Base#BASE Object -- @param Core.Base#BASE Object The object itself.
-- @return Core.Base#BASE The added BASE Object. -- @return Core.Base#BASE The added BASE Object.
function SET_BASE:Add( ObjectName, Object ) function SET_BASE:Add( ObjectName, Object )
self:F2( { ObjectName = ObjectName, Object = Object } ) self:F2( { ObjectName = ObjectName, Object = Object } )
@ -234,9 +239,14 @@ do -- SET_BASE
if self.Set[ObjectName] then if self.Set[ObjectName] then
self:Remove( ObjectName, true ) self:Remove( ObjectName, true )
end end
-- Add object to set.
self.Set[ObjectName] = Object self.Set[ObjectName] = Object
-- Add Object name to Index.
table.insert( self.Index, ObjectName ) table.insert( self.Index, ObjectName )
-- Trigger Added event.
self:Added( ObjectName, Object ) self:Added( ObjectName, Object )
end end
@ -253,6 +263,81 @@ do -- SET_BASE
end end
--- Get the *union* of two sets.
-- @param #SET_BASE self
-- @param Core.Set#SET_BASE SetB Set *B*.
-- @return Core.Set#SET_BASE The union set, i.e. contains objects that are in set *A* **or** in set *B*.
function SET_BASE:GetSetUnion(SetB)
local union=SET_BASE:New()
for _,ObjectA in pairs(self.Set) do
union:AddObject(ObjectA)
end
for _,ObjectB in pairs(SetB.Set) do
union:AddObject(ObjectB)
end
return union
end
--- Get the *intersection* of this set, called *A*, and another set.
-- @param #SET_BASE self
-- @param Core.Set#SET_BASE SetB Set other set, called *B*.
-- @return Core.Set#SET_BASE A set of objects that is included in set *A* **and** in set *B*.
function SET_BASE:GetSetIntersection(SetB)
local intersection=SET_BASE:New()
local union=self:GetSetUnion(SetB)
for _,Object in pairs(union.Set) do
if self:IsIncludeObject(Object) and SetB:IsIncludeObject(Object) then
intersection:AddObject(intersection)
end
end
return intersection
end
--- Get the *complement* of two sets.
-- @param #SET_BASE self
-- @param Core.Set#SET_BASE SetB Set other set, called *B*.
-- @return Core.Set#SET_BASE The set of objects that are in set *B* but **not** in this set *A*.
function SET_BASE:GetSetComplement(SetB)
local complement=SET_BASE:New()
local union=self:GetSetUnion(SetA, SetB)
for _,Object in pairs(union.Set) do
if SetA:IsIncludeObject(Object) and SetB:IsIncludeObject(Object) then
intersection:Add(intersection)
end
end
return intersection
end
--- Compare two sets.
-- @param #SET_BASE self
-- @param Core.Set#SET_BASE SetA First set.
-- @param Core.Set#SET_BASE SetB Set to be merged into first set.
-- @return Core.Set#SET_BASE The set of objects that are included in SetA and SetB.
function SET_BASE:CompareSets(SetA, SetB)
for _,ObjectB in pairs(SetB.Set) do
if SetA:IsIncludeObject(ObjectB) then
SetA:Add(ObjectB)
end
end
return SetA
end
@ -712,7 +797,7 @@ do -- SET_BASE
--end --end
--- Decides whether to include the Object --- Decides whether to include the Object.
-- @param #SET_BASE self -- @param #SET_BASE self
-- @param #table Object -- @param #table Object
-- @return #SET_BASE self -- @return #SET_BASE self
@ -721,6 +806,16 @@ do -- SET_BASE
return true return true
end end
--- Decides whether to include the Object.
-- @param #SET_BASE self
-- @param #table Object
-- @return #SET_BASE self
function SET_BASE:IsInSet(ObjectName)
self:F3( Object )
return true
end
--- Gets a string with all the object names. --- Gets a string with all the object names.
-- @param #SET_BASE self -- @param #SET_BASE self
@ -965,7 +1060,7 @@ do -- SET_GROUP
-- Note that for each unit in the group that is set, a default cargo bay limit is initialized. -- Note that for each unit in the group that is set, a default cargo bay limit is initialized.
-- @param Core.Set#SET_GROUP self -- @param Core.Set#SET_GROUP self
-- @param Wrapper.Group#GROUP group The group which should be added to the set. -- @param Wrapper.Group#GROUP group The group which should be added to the set.
-- @return self -- @return Core.Set#SET_GROUP self
function SET_GROUP:AddGroup( group ) function SET_GROUP:AddGroup( group )
self:Add( group:GetName(), group ) self:Add( group:GetName(), group )
@ -981,7 +1076,7 @@ do -- SET_GROUP
--- Add GROUP(s) to SET_GROUP. --- Add GROUP(s) to SET_GROUP.
-- @param Core.Set#SET_GROUP self -- @param Core.Set#SET_GROUP self
-- @param #string AddGroupNames A single name or an array of GROUP names. -- @param #string AddGroupNames A single name or an array of GROUP names.
-- @return self -- @return Core.Set#SET_GROUP self
function SET_GROUP:AddGroupsByName( AddGroupNames ) function SET_GROUP:AddGroupsByName( AddGroupNames )
local AddGroupNamesArray = ( type( AddGroupNames ) == "table" ) and AddGroupNames or { AddGroupNames } local AddGroupNamesArray = ( type( AddGroupNames ) == "table" ) and AddGroupNames or { AddGroupNames }
@ -996,7 +1091,7 @@ do -- SET_GROUP
--- Remove GROUP(s) from SET_GROUP. --- Remove GROUP(s) from SET_GROUP.
-- @param Core.Set#SET_GROUP self -- @param Core.Set#SET_GROUP self
-- @param Wrapper.Group#GROUP RemoveGroupNames A single name or an array of GROUP names. -- @param Wrapper.Group#GROUP RemoveGroupNames A single name or an array of GROUP names.
-- @return self -- @return Core.Set#SET_GROUP self
function SET_GROUP:RemoveGroupsByName( RemoveGroupNames ) function SET_GROUP:RemoveGroupsByName( RemoveGroupNames )
local RemoveGroupNamesArray = ( type( RemoveGroupNames ) == "table" ) and RemoveGroupNames or { RemoveGroupNames } local RemoveGroupNamesArray = ( type( RemoveGroupNames ) == "table" ) and RemoveGroupNames or { RemoveGroupNames }
@ -1909,8 +2004,8 @@ do -- SET_UNIT
--- Remove UNIT(s) from SET_UNIT. --- Remove UNIT(s) from SET_UNIT.
-- @param Core.Set#SET_UNIT self -- @param Core.Set#SET_UNIT self
-- @param Wrapper.Unit#UNIT RemoveUnitNames A single name or an array of UNIT names. -- @param #table RemoveUnitNames A single name or an array of UNIT names.
-- @return self -- @return Core.Set#SET_UNIT self
function SET_UNIT:RemoveUnitsByName( RemoveUnitNames ) function SET_UNIT:RemoveUnitsByName( RemoveUnitNames )
local RemoveUnitNamesArray = ( type( RemoveUnitNames ) == "table" ) and RemoveUnitNames or { RemoveUnitNames } local RemoveUnitNamesArray = ( type( RemoveUnitNames ) == "table" ) and RemoveUnitNames or { RemoveUnitNames }
@ -2080,7 +2175,23 @@ do -- SET_UNIT
return self return self
end end
--- Iterate the SET_UNIT and count how many UNITs are alive.
-- @param #SET_UNIT self
-- @return #number The number of UNITs alive.
function SET_UNIT:CountAlive()
local Set = self:GetSet()
local CountU = 0
for UnitID, UnitData in pairs(Set) do -- For each GROUP in SET_GROUP
if UnitData and UnitData:IsAlive() then
CountU = CountU + 1
end
end
return CountU
end
--- Starts the filtering. --- Starts the filtering.
-- @param #SET_UNIT self -- @param #SET_UNIT self
@ -3099,6 +3210,24 @@ do -- SET_STATIC
return self return self
end end
--- Iterate the SET_STATIC and count how many STATICSs are alive.
-- @param #SET_STATIC self
-- @return #number The number of UNITs alive.
function SET_STATIC:CountAlive()
local Set = self:GetSet()
local CountU = 0
for UnitID, UnitData in pairs(Set) do
if UnitData and UnitData:IsAlive() then
CountU = CountU + 1
end
end
return CountU
end
--- Handles the Database to check on an event (birth) that the Object was added in the Database. --- Handles the Database to check on an event (birth) that the Object was added in the Database.
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! -- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
-- @param #SET_STATIC self -- @param #SET_STATIC self

View File

@ -0,0 +1,600 @@
--- **Ops** - Office of Military Intelligence.
--
-- **Main Features:**
--
-- * Stuff
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Ops.Intel
-- @image OPS_Intel.png
--- INTEL class.
-- @type INTEL
-- @field #string ClassName Name of the class.
-- @field #boolean Debug Debug mode. Messages to all about status.
-- @field #string lid Class id string for output to DCS log file.
-- @field #number coalition Coalition side number, e.g. `coalition.side.RED`.
-- @field #string alias Name of the agency.
-- @field #table filterCategory Category filters.
-- @field Core.Set#SET_ZONE acceptzoneset Set of accept zones. If defined, only contacts in these zones are considered.
-- @field Core.Set#SET_ZONE rejectzoneset Set of reject zones. Contacts in these zones are not considered, even if they are in accept zones.
-- @field #table Contacts Table of detected items.
-- @field #table ContactsLost Table of lost detected items.
-- @field #table ContactsUnknown Table of new detected items.
-- @field #number dTforget Time interval in seconds before a known contact which is not detected any more is forgotten.
-- @extends Core.Fsm#FSM
--- Top Secret!
--
-- ===
--
-- ![Banner Image](..\Presentations\CarrierAirWing\INTEL_Main.jpg)
--
-- # The INTEL Concept
--
--
--
-- @field #INTEL
INTEL = {
ClassName = "INTEL",
Debug = nil,
lid = nil,
alias = nil,
filterCategory = {},
detectionset = nil,
Contacts = {},
ContactsLost = {},
ContactsUnknown = {},
}
--- Detected item info.
-- @type INTEL.DetectedItem
-- @field #string groupname Name of the group.
-- @field Wrapper.Group#GROUP group The contact group.
-- @field #string typename Type name of detected item.
-- @field #number category Category number.
-- @field #string categoryname Category name.
-- @field #string attribute Generalized attribute.
-- @field #number threatlevel Threat level of this item.
-- @field #number Tdetected Time stamp in abs. mission time seconds when this item was last detected.
-- @field Core.Point#COORDINATE position Last known position of the item.
-- @field DCS#Vec3 velocity 3D velocity vector. Components x,y and z in m/s.
-- @field #number speed Last known speed.
-- @field #number markerID F10 map marker ID.
--- INTEL class version.
-- @field #string version
INTEL.version="0.0.3"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- DONE: Accept zones.
-- TODO: Reject zones.
-- TODO: Filter detection methods.
-- NOGO: SetAttributeZone --> return groups of generalized attributes in a zone.
-- DONE: Loose units only if they remain undetected for a given time interval. We want to avoid fast oscillation between detected/lost states. Maybe 1-5 min would be a good time interval?!
-- DONE: Combine units to groups for all, new and lost.
-- TODO: process detected set asynchroniously for better performance.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new INTEL object and start the FSM.
-- @param #INTEL self
-- @param Core.Set#SET_GROUP DetectionSet Set of detection groups.
-- @param #number Coalition Coalition side. Can also be passed as a string "red", "blue" or "neutral".
-- @return #INTEL self
function INTEL:New(DetectionSet, Coalition)
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #INTEL
-- Detection set.
self.detectionset=DetectionSet or SET_GROUP:New()
-- Determine coalition from first group in set.
self.coalition=Coalition or DetectionSet:CountAlive()>0 and DetectionSet:GetFirst():GetCoalition() or nil
-- Set alias.
self.alias="SPECTRE"
if self.coalition then
if self.coalition==coalition.side.RED then
self.alias="KGB"
elseif self.coalition==coalition.side.BLUE then
self.alias="CIA"
end
end
-- Set some string id for output to DCS.log file.
self.lid=string.format("INTEL %s | ", self.alias)
-- Start State.
self:SetStartState("Stopped")
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
self:AddTransition("*", "Status", "*") -- INTEL status update
self:AddTransition("*", "Detect", "*") -- Start detection run. Not implemented yet!
self:AddTransition("*", "NewContact", "*") -- New contact has been detected.
self:AddTransition("*", "LostContact", "*") -- Contact could not be detected any more.
-- Defaults
self:SetForgetTime()
self:SetAcceptZones()
------------------------
--- Pseudo Functions ---
------------------------
--- Triggers the FSM event "Start". Starts the INTEL. Initializes parameters and starts event handlers.
-- @function [parent=#INTEL] Start
-- @param #INTEL self
--- Triggers the FSM event "Start" after a delay. Starts the INTEL. Initializes parameters and starts event handlers.
-- @function [parent=#INTEL] __Start
-- @param #INTEL self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Stop". Stops the INTEL and all its event handlers.
-- @param #INTEL self
--- Triggers the FSM event "Stop" after a delay. Stops the INTEL and all its event handlers.
-- @function [parent=#INTEL] __Stop
-- @param #INTEL self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Status".
-- @function [parent=#INTEL] Status
-- @param #INTEL self
--- Triggers the FSM event "Status" after a delay.
-- @function [parent=#INTEL] __Status
-- @param #INTEL self
-- @param #number delay Delay in seconds.
-- Debug trace.
if false then
self.Debug=true
BASE:TraceOnOff(true)
BASE:TraceClass(self.ClassName)
BASE:TraceLevel(1)
end
self.Debug=true
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set accept zones. Only contacts detected in this/these zone(s) are considered.
-- @param #INTEL self
-- @param Core.Set#SET_ZONE AcceptZoneSet Set of accept zones
-- @return #INTEL self
function INTEL:SetAcceptZones(AcceptZoneSet)
self.acceptzoneset=AcceptZoneSet or SET_ZONE:New()
return self
end
--- Set accept zones. Only contacts detected in this zone are considered.
-- @param #INTEL self
-- @param Core.Zone#ZONE AcceptZone Add a zone to the accept zone set.
-- @return #INTEL self
function INTEL:AddAcceptZone(AcceptZone)
self.acceptzoneset:AddZone(AcceptZone)
return self
end
--- Set forget contacts time interval.
-- 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.
-- @param #INTEL self
-- @param #number TimeInterval Time interval in seconds. Default is 120 sec.
-- @return #INTEL self
function INTEL:SetForgetTime(TimeInterval)
self.dTforget=TimeInterval or 120
return self
end
--- Filter unit categories. Valid categories are:
--
-- * Unit.Category.AIRPLANE
-- * Unit.Category.HELICOPTER
-- * Unit.Category.GROUND_UNIT
-- * Unit.Category.SHIP
-- * Unit.Category.STRUCTURE
--
-- @param #INTEL self
-- @param #table Categories Filter categories, e.g. {Unit.Category.AIRPLANE, Unit.Category.HELICOPTER}.
-- @return #INTEL self
function INTEL:SetFilterCategory(Categories)
if type(Categories)~="table" then
Categories={Categories}
end
self.filterCategory=Categories
local text="Filter categories: "
for _,category in pairs(self.filterCategory) do
text=text..string.format("%d,", category)
end
self:I(self.lid..text)
return self
end
--- Filter group categories. Valid categories are:
--
-- * Group.Category.AIRPLANE
-- * Group.Category.HELICOPTER
-- * Group.Category.GROUND
-- * Group.Category.SHIP
-- * Group.Category.TRAIN
--
-- @param #INTEL self
-- @param #table Categories Filter categories, e.g. {Group.Category.AIRPLANE, Group.Category.HELICOPTER}.
-- @return #INTEL self
function INTEL:FilterCategoryGroup(GroupCategories)
if type(GroupCategories)~="table" then
GroupCategories={GroupCategories}
end
self.filterCategoryGroup=GroupCategories
local text="Filter group categories: "
for _,category in pairs(self.filterCategoryGroup) do
text=text..string.format("%d,", category)
end
self:I(self.lid..text)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start & Status
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after Start event. Starts the FLIGHTGROUP FSM and event handlers.
-- @param #INTEL self
-- @param Wrapper.Group#GROUP Group Flight group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function INTEL:onafterStart(From, Event, To)
-- Short info.
local text=string.format("Starting INTEL v%s", self.version)
self:I(self.lid..text)
-- Start the status monitoring.
self:__Status(-math.random(10))
end
--- On after "Status" event.
-- @param #INTEL self
-- @param Wrapper.Group#GROUP Group Flight group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function INTEL:onafterStatus(From, Event, To)
-- FSM state.
local fsmstate=self:GetState()
-- Fresh arrays.
self.ContactsLost={}
self.ContactsUnknown={}
-- Check if group has detected any units.
self:UpdateIntel()
-- Number of total contacts.
local Ncontacts=#self.Contacts
-- Short info.
local text=string.format("Status %s [Agents=%s]: Contacts=%d, New=%d, Lost=%d", fsmstate, self.detectionset:CountAlive(), Ncontacts, #self.ContactsUnknown, #self.ContactsLost)
self:I(self.lid..text)
-- Detailed info.
if Ncontacts>0 then
text="Detected Contacts:"
for _,_contact in pairs(self.Contacts) do
local contact=_contact --#INTEL.DetectedItem
local dT=timer.getAbsTime()-contact.Tdetected
text=text..string.format("\n- %s (%s): %s, units=%d, T=%d sec", contact.categoryname, contact.attribute, contact.groupname, contact.group:CountAliveUnits(), dT)
if contact.mission then
local mission=contact.mission --Ops.Auftrag#AUFTRAG
text=text..string.format(" mission name=%s type=%s target=%s", mission.name, mission.type, mission:GetTargetName() or "unkown")
end
end
self:I(self.lid..text)
end
self:__Status(-30)
end
--- Update detected items.
-- @param #INTEL self
function INTEL:UpdateIntel()
-- Set of all detected units.
local DetectedSet=SET_UNIT:New()
-- Loop over all units providing intel.
for _,_group in pairs(self.detectionset:GetSet()) do
local group=_group --Wrapper.Group#GROUP
if group and group:IsAlive() then
for _,_recce in pairs(group:GetUnits()) do
local recce=_recce --Wrapper.Unit#UNIT
-- Get set of detected units.
local detectedunitset=recce:GetDetectedUnitSet()
-- Add detected units to all set.
DetectedSet=DetectedSet:GetSetUnion(detectedunitset)
end
end
end
-- TODO: Filter units from reject zones.
-- TODO: Filter detection methods?
local remove={}
for _,_unit in pairs(DetectedSet.Set) do
local unit=_unit --Wrapper.Unit#UNIT
-- Check if unit is in any of the accept zones.
if self.acceptzoneset:Count()>0 then
local inzone=false
for _,_zone in pairs(self.acceptzoneset.Set) do
local zone=_zone --Core.Zone#ZONE
if unit:IsInZone(zone) then
inzone=true
break
end
end
-- Unit is not in accept zone ==> remove!
if not inzone then
table.insert(remove, unit:GetName())
end
end
-- Filter unit categories.
if #self.filterCategory>0 then
local unitcategory=unit:GetUnitCategory()
local keepit=false
for _,filtercategory in pairs(self.filterCategory) do
if unitcategory==filtercategory then
keepit=true
break
end
end
if not keepit then
self:I(self.lid..string.format("Removing unit %s category=%d", unit:GetName(), unit:GetCategory()))
table.insert(remove, unit:GetName())
end
end
end
-- Remove filtered units.
for _,unitname in pairs(remove) do
DetectedSet:Remove(unitname, true)
end
-- Create detected contacts.
self:CreateDetectedItems(DetectedSet)
end
--- Create detected items.
-- @param #INTEL self
-- @param Core.Set#SET_UNIT detectedunitset Set of detected units.
function INTEL:CreateDetectedItems(detectedunitset)
local detectedgroupset=SET_GROUP:New()
-- Convert detected UNIT set to detected GROUP set.
for _,_unit in pairs(detectedunitset:GetSet()) do
local unit=_unit --Wrapper.Unit#UNIT
local group=unit:GetGroup()
if group and group:IsAlive() then
local groupname=group:GetName()
detectedgroupset:Add(groupname, group)
end
end
-- Current time.
local Tnow=timer.getAbsTime()
for _,_group in pairs(detectedgroupset.Set) do
local group=_group --Wrapper.Group#GROUP
-- Group name.
local groupname=group:GetName()
-- Get contact if already known.
local detecteditem=self:GetContactByName(groupname)
if detecteditem then
---
-- Detected item already exists ==> Update data.
---
detecteditem.Tdetected=Tnow
detecteditem.position=group:GetCoordinate()
detecteditem.velocity=group:GetVelocityVec3()
detecteditem.speed=group:GetVelocityMPS()
else
---
-- Detected item does not exist in our list yet.
---
-- Create new contact.
local item={} --#INTEL.DetectedItem
item.groupname=groupname
item.group=group
item.Tdetected=Tnow
item.typename=group:GetTypeName()
item.attribute=group:GetAttribute()
item.category=group:GetCategory()
item.categoryname=group:GetCategoryName()
item.threatlevel=group:GetUnit(1):GetThreatLevel()
item.position=group:GetCoordinate()
item.velocity=group:GetVelocityVec3()
item.speed=group:GetVelocityMPS()
-- Add contact to table.
self:AddContact(item)
-- Trigger new contact event.
self:NewContact(item)
end
end
-- Now check if there some groups could not be detected any more.
for i=#self.Contacts,1,-1 do
local item=self.Contacts[i] --#INTEL.DetectedItem
local group=detectedgroupset:FindGroup(item.groupname)
-- Check if deltaT>Tforget. We dont want quick oscillations between detected and undetected states.
if self:CheckContactLost(item) then
-- Trigger LostContact event. This also adds the contact to the self.ContactsLost table.
self:LostContact(item)
-- Remove contact from table.
self:RemoveContact(item)
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Events
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after "NewContact" event.
-- @param #INTEL self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #INTEL.DetectedItem Contact Detected contact.
function INTEL:onafterNewContact(From, Event, To, Contact)
self:I(self.lid..string.format("NEW contact %s", Contact.groupname))
table.insert(self.ContactsUnknown, Contact)
end
--- On after "LostContact" event.
-- @param #INTEL self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #INTEL.DetectedItem Contact Detected contact.
function INTEL:onafterLostContact(From, Event, To, Contact)
self:I(self.lid..string.format("LOST contact %s", Contact.groupname))
table.insert(self.ContactsLost, Contact)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create detected items.
-- @param #INTEL self
-- @param #string groupname Name of the contact group.
-- @return #INTEL.DetectedItem
function INTEL:GetContactByName(groupname)
for i,_contact in pairs(self.Contacts) do
local contact=_contact --#INTEL.DetectedItem
if contact.groupname==groupname then
return contact
end
end
return nil
end
--- Add a contact to our list.
-- @param #INTEL self
-- @param #INTEL.DetectedItem Contact The contact to be removed.
function INTEL:AddContact(Contact)
table.insert(self.Contacts, Contact)
end
--- Remove a contact from our list.
-- @param #INTEL self
-- @param #INTEL.DetectedItem Contact The contact to be removed.
function INTEL:RemoveContact(Contact)
for i,_contact in pairs(self.Contacts) do
local contact=_contact --#INTEL.DetectedItem
if contact.groupname==Contact.groupname then
table.remove(self.Contacts, i)
end
end
end
--- Remove a contact from our list.
-- @param #INTEL self
-- @param #INTEL.DetectedItem Contact The contact to be removed.
-- @return #boolean If true, contact was not detected for at least *dTforget* seconds.
function INTEL:CheckContactLost(Contact)
-- Group dead?
if Contact.group==nil or not Contact.group:IsAlive() then
return true
end
-- Time since last detected.
local dT=timer.getAbsTime()-Contact.Tdetected
local dTforget=self.dTforget
if Contact.category==Group.Category.GROUND then
dTforget=60*60*2 -- 2 hours
elseif Contact.category==Group.Category.AIRPLANE then
dTforget=60*10 -- 10 min
elseif Contact.category==Group.Category.HELICOPTER then
dTforget=60*20 -- 20 min
elseif Contact.category==Group.Category.SHIP then
dTforget=60*60 -- 1 hour
elseif Contact.category==Group.Category.TRAIN then
dTforget=60*60 -- 1 hour
end
if dT>dTforget then
return true
else
return false
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------