This commit is contained in:
Frank 2020-10-19 23:33:39 +02:00
parent 277eb2d09f
commit 459ff26868
4 changed files with 245 additions and 117 deletions

View File

@ -913,7 +913,7 @@ do -- FSM
--- Check if FSM is in state.
-- @param #FSM self
-- @param #string State State name.
-- @param #boolean If true, FSM is in this state.
-- @return #boolean If true, FSM is in this state.
function FSM:Is( State )
return self.current == State
end
@ -921,7 +921,7 @@ do -- FSM
--- Check if FSM is in state.
-- @param #FSM self
-- @param #string State State name.
-- @param #boolean If true, FSM is in this state.
-- @return #boolean If true, FSM is in this state.
function FSM:is(state)
return self.current == state
end

View File

@ -102,6 +102,10 @@ function ARMYGROUP:New(Group)
self:AddTransition("*", "Detour", "OnDetour") -- Make a detour to a coordinate and resume route afterwards.
self:AddTransition("OnDetour", "DetourReached", "Cruising") -- Group reached the detour coordinate.
self:AddTransition("*", "Rearm", "Rearm") -- Group is send to a coordinate and waits until ammo is refilled.
self:AddTransition("Rearm", "Rearming", "Rearming") -- Group has arrived at the rearming coodinate and is waiting to be fully rearmed.
self:AddTransition("Rearming", "Rearmed", "Cruising") -- Group was rearmed.
------------------------
--- Pseudo Functions ---
------------------------
@ -294,23 +298,14 @@ function ARMYGROUP:onafterStatus(From, Event, To)
self:_CheckDetectedUnits()
end
if self:IsRearming() then
-- Check ammo status.
self:_CheckAmmoStatus()
local rearmed=self:_CheckAmmoFull()
-- Update position etc.
self:_UpdatePosition()
if rearmed then
self:Rearmed()
end
else
-- Update position etc.
self:_UpdatePosition()
-- Check if group got stuck.
self:_CheckStuck()
end
-- Check if group got stuck.
self:_CheckStuck()
if self.verbose>=1 then
@ -325,8 +320,8 @@ function ARMYGROUP:onafterStatus(From, Event, To)
local formation=self.option.Formation or "unknown"
-- Info text.
local text=string.format("%s: Wp=%d/%d-->%d Speed=%.1f (%d) Heading=%03d ROE=%d Alarm=%d Formation=%s Tasks=%d Missions=%d",
fsmstate, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), speed, speedEx, self.heading, roe, alarm, formation, nTaskTot, nMissions)
local text=string.format("%s [ROE-AS=%d-%d T/M=%d/%d]: Wp=%d/%d-->%d (final %s), Speed=%.1f (%d), Heading=%03d",
fsmstate, roe, alarm, nTaskTot, nMissions, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), tostring(self.passedfinalwp), speed, speedEx, self.heading)
self:I(self.lid..text)
end
@ -398,6 +393,11 @@ function ARMYGROUP:onafterSpawned(From, Event, To)
self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, true)
end
-- Formation
if not self.option.Formation then
self.option.Formation=self.optionDefault.Formation
end
end
-- Update route.
@ -419,6 +419,10 @@ end
-- @param #number Formation Formation of the group.
function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation)
-- Debug info.
local text=string.format("Update route n=%s, Speed=%s, Formation=%s", tostring(n), tostring(Speed), tostring(Formation))
self:T(self.lid..text)
-- Update route from this waypoint number onwards.
n=n or self:GetWaypointIndexNext(self.adinfinitum)
@ -495,9 +499,6 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation)
local wp=_wp
local text=string.format("WP #%d UID=%d type=%s: Speed=%d m/s, alt=%d m, Action=%s", i, wp.uid and wp.uid or 0, wp.type, wp.speed, wp.alt, wp.action)
self:T(text)
if false and wp.coordinate then
wp.coordinate:MarkToAll(text)
end
end
end
@ -523,6 +524,43 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation)
end
--- On after "GotoWaypoint" event. Group will got to the given waypoint and execute its route from there.
-- @param #ARMYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number UID The goto waypoint unique ID.
-- @param #number Speed (Optional) Speed to waypoint in knots.
-- @param #number Formation (Optional) Formation to waypoint.
function ARMYGROUP:onafterGotoWaypoint(From, Event, To, UID, Speed, Formation)
local n=self:GetWaypointIndex(UID)
--env.info(string.format("FF AG Goto waypoint UID=%s Index=%s, Speed=%s, Formation=%s", tostring(UID), tostring(n), tostring(Speed), tostring(Formation)))
if n then
-- TODO: switch to re-enable waypoint tasks.
if false then
local tasks=self:GetTasksWaypoint(n)
for _,_task in pairs(tasks) do
local task=_task --#OPSGROUP.Task
task.status=OPSGROUP.TaskStatus.SCHEDULED
end
end
-- Speed to waypoint.
Speed=Speed or self:GetSpeedToWaypoint(n)
-- Update the route.
self:UpdateRoute(n, Speed, Formation)
end
end
--- On after "Detour" event.
-- @param #ARMYGROUP self
-- @param #string From From state.
@ -792,7 +830,7 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation
end
-- Debug info.
self:I(self.lid..string.format("Adding waypoint UID=%d (index=%d), Speed=%.1f knots, Dist2Road=%d m, Action=%s", waypoint.uid, wpnumber, Speed, waypoint.roaddist, waypoint.action))
self:T(self.lid..string.format("Adding waypoint UID=%d (index=%d), Speed=%.1f knots, Dist2Road=%d m, Action=%s", waypoint.uid, wpnumber, Speed, waypoint.roaddist, waypoint.action))
-- Update route.
if Updateroute==nil or Updateroute==true then
@ -876,9 +914,11 @@ function ARMYGROUP:_InitGroup()
element.ammo0=self:GetAmmoUnit(unit, false)
-- Debug text.
local text=string.format("Adding element %s: status=%s, skill=%s, category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)",
element.name, element.status, element.skill, element.categoryname, element.category, element.size, element.length, element.height, element.width)
self:I(self.lid..text)
if self.verbose>=2 then
local text=string.format("Adding element %s: status=%s, skill=%s, category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)",
element.name, element.status, element.skill, element.categoryname, element.category, element.size, element.length, element.height, element.width)
self:I(self.lid..text)
end
-- Add element to table.
table.insert(self.elements, element)

View File

@ -883,7 +883,7 @@ function NAVYGROUP:onafterTurnIntoWindOver(From, Event, To, IntoWindData)
---
-- Detour to where we left the route.
self:I(self.lid.."FF Turn Into Wind Over ==> Uturn!")
self:T(self.lid.."FF Turn Into Wind Over ==> Uturn!")
self:Detour(self.intowind.Coordinate, self:GetSpeedCruise(), 0, true)
else
@ -897,7 +897,7 @@ function NAVYGROUP:onafterTurnIntoWindOver(From, Event, To, IntoWindData)
local speed=self:GetWaypointSpeed(indx)
-- Update route.
self:I(self.lid..string.format("FF Turn Into Wind Over ==> Next WP Index=%d at %.1f knots via update route!", indx, speed))
self:T(self.lid..string.format("FF Turn Into Wind Over ==> Next WP Index=%d at %.1f knots via update route!", indx, speed))
self:__UpdateRoute(-1, indx, speed)
end
@ -1238,51 +1238,64 @@ function NAVYGROUP:_InitGroup()
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
-- Get unit template.
local unittemplate=unit:GetTemplate()
local element={} --#NAVYGROUP.Element
element.name=unit:GetName()
element.typename=unit:GetTypeName()
element.status=OPSGROUP.ElementStatus.INUTERO
element.unit=unit
element.status=OPSGROUP.ElementStatus.INUTERO
element.typename=unit:GetTypeName()
element.skill=unittemplate.skill or "Unknown"
element.ai=true
element.category=element.unit:GetUnitCategory()
element.categoryname=element.unit:GetCategoryName()
element.size, element.length, element.height, element.width=unit:GetObjectSize()
element.ammo0=self:GetAmmoUnit(unit, false)
-- Debug text.
if self.verbose>=2 then
local text=string.format("Adding element %s: status=%s, skill=%s, category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)",
element.name, element.status, element.skill, element.categoryname, element.category, element.size, element.length, element.height, element.width)
self:I(self.lid..text)
end
-- Add element to table.
table.insert(self.elements, element)
self:GetAmmoUnit(unit, false)
-- Get Descriptors.
self.descriptors=self.descriptors or unit:GetDesc()
-- Set type name.
self.actype=self.actype or unit:GetTypeName()
if unit:IsAlive() then
-- Trigger spawned event.
self:ElementSpawned(element)
end
end
-- Get first unit. This is used to extract other parameters.
local unit=self.group:GetUnit(1)
if unit then
self.descriptors=unit:GetDesc()
self.actype=unit:GetTypeName()
-- Debug info.
if self.verbose>=1 then
local text=string.format("Initialized Navy Group %s:\n", self.groupname)
text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))
text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos)
text=text..string.format("FSM state = %s\n", self:GetState())
text=text..string.format("Is alive = %s\n", tostring(self:IsAlive()))
text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated()))
self:I(self.lid..text)
end
-- Init done.
self.groupinitialized=true
-- Debug info.
if self.verbose>=1 then
local text=string.format("Initialized Navy Group %s:\n", self.groupname)
text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))
text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos)
text=text..string.format("FSM state = %s\n", self:GetState())
text=text..string.format("Is alive = %s\n", tostring(self:IsAlive()))
text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated()))
self:I(self.lid..text)
end
-- Init done.
self.groupinitialized=true
return self
end
@ -1444,7 +1457,7 @@ function NAVYGROUP:_CheckTurnsIntoWind()
else
-- Get next window.
local IntoWind=self:GetNextTurnIntoWind()
local IntoWind=self:GetTurnIntoWindNext()
-- Start turn into wind.
if IntoWind then

View File

@ -87,6 +87,8 @@
--
-- @field #OPSGROUP.Spot spot Laser and IR spot.
--
-- @field #OPSGROUP.Ammo ammo Initial ammuont of ammo.
--
-- @extends Core.Fsm#FSM
--- *A small group of determined and like-minded people can change the course of history.* --- Mahatma Gandhi
@ -312,6 +314,8 @@ OPSGROUP.version="0.6.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Suppression of fire.
-- TODO: AI on/off.
-- TODO: Invisible/immortal.
-- TODO: Add pseudo function.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -387,10 +391,6 @@ function OPSGROUP:New(Group)
self:AddTransition("*", "EnterZone", "*") -- Group entered a certain zone.
self:AddTransition("*", "LeaveZone", "*") -- Group leaves a certain zone.
self:AddTransition("*", "Rearm", "Rearming") -- Group is send to a coordinate and waits until ammo is refilled.
self:AddTransition("Rearming", "Rearming", "Rearming") -- Group has arrived at the rearming coodinate and is waiting to be fully rearmed.
self:AddTransition("Rearming", "Rearmed", "Cruising") -- Group was rearmed.
self:AddTransition("*", "LaserOn", "*") -- Turn laser on.
self:AddTransition("*", "LaserOff", "*") -- Turn laser off.
self:AddTransition("*", "LaserCode", "*") -- Switch laser code.
@ -577,6 +577,13 @@ function OPSGROUP:GetDetectedUnits()
return self.detectedunits
end
--- Get inital amount of ammunition.
-- @param #OPSGROUP self
-- @return #OPSGROUP.Ammo Initial ammo table.
function OPSGROUP:GetAmmo0()
return self.ammo
end
--- Get highest detected threat. Detection must be turned on. The threat level is a number between 0 and 10, where 0 is the lowest, e.g. unarmed units.
-- @param #OPSGROUP self
-- @param #number ThreatLevelMin Only consider threats with level greater or equal to this number. Default 1 (so unarmed units wont be considered).
@ -611,7 +618,6 @@ function OPSGROUP:GetThreat(ThreatLevelMin, ThreatLevelMax)
return threat, level
end
--- Get MOOSE GROUP object.
-- @param #OPSGROUP self
-- @return Wrapper.Group#GROUP Moose group object.
@ -1089,7 +1095,8 @@ end
-- @param #OPSGROUP self
-- @return #boolean If true, group is rearming.
function OPSGROUP:IsRearming()
return self:Is("Rearming")
local rearming=self:Is("Rearming") or self:Is("Rearm")
return rearming
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -2814,7 +2821,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
end
--- On after "GotoWaypoint" event. Group will got to the given waypoint and execute its route from there.
--- Set tasks at this waypoint
-- @param #OPSGROUP self
-- @param #OPSGROUP.Waypoint Waypoint The waypoint.
-- @return #number Number of tasks.
@ -3363,6 +3370,8 @@ function OPSGROUP:_CheckGroupDone(delay)
-- Get current waypoint.
local waypoint=self:GetWaypoint(self.currentwp)
--env.info("FF CheckGroupDone")
if waypoint then
-- Number of tasks remaining for this waypoint.
@ -3439,6 +3448,56 @@ function OPSGROUP:_CheckGroupDone(delay)
end
--- Check if group got stuck.
-- @param #OPSGROUP self
function OPSGROUP:_CheckStuck()
-- Holding means we are not stuck.
if self:IsHolding() or self:Is("Rearming") then
return
end
-- Current time.
local Tnow=timer.getTime()
-- Expected speed in m/s.
local ExpectedSpeed=self:GetExpectedSpeed()
-- Current speed in m/s.
local speed=self:GetVelocity()
-- Check speed.
if speed<0.5 then
if ExpectedSpeed>0 and not self.stuckTimestamp then
self:T2(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected", speed, ExpectedSpeed))
self.stuckTimestamp=Tnow
self.stuckVec3=self:GetVec3()
end
else
-- Moving (again).
self.stuckTimestamp=nil
end
-- Somehow we are not moving...
if self.stuckTimestamp then
-- Time we are holding.
local holdtime=Tnow-self.stuckTimestamp
if holdtime>=10*60 then
self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime))
--TODO: Stuck event!
end
end
end
--- Check ammo is full.
-- @param #OPSGROUP self
-- @return #boolean If true, ammo is full.
@ -3459,6 +3518,72 @@ function OPSGROUP:_CheckAmmoFull()
return true
end
--- Check ammo status.
-- @param #OPSGROUP self
function OPSGROUP:_CheckAmmoStatus()
-- First check if there was ammo initially.
if self.ammo.Total>0 then
-- Get current ammo.
local ammo=self:GetAmmoTot()
-- Check if rearming is completed.
if self:IsRearming() then
if ammo.Total==self.ammo.Total then
self:Rearmed()
end
end
-- Total.
if self.outofAmmo and ammo.Total>0 then
self.outofAmmo=false
end
if ammo.Total==0 and not self.outofAmmo then
self.outofAmmo=true
self:OutOfAmmo()
end
-- Guns.
if self.outofGuns and ammo.Guns>0 then
self.outoffGuns=false
end
if ammo.Guns==0 and self.ammo.Guns>0 and not self.outofGuns then
self.outofGuns=true
self:OutOfGuns()
end
-- Rockets.
if self.outofRockets and ammo.Rockets>0 then
self.outoffRockets=false
end
if ammo.Rockets==0 and self.ammo.Rockets>0 and not self.outofRockets then
self.outofRockets=true
self:OutOfRockets()
end
-- Bombs.
if self.outofBombs and ammo.Bombs>0 then
self.outoffBombs=false
end
if ammo.Bombs==0 and self.ammo.Bombs>0 and not self.outofBombs then
self.outofBombs=true
self:OutOfBombs()
end
-- Missiles.
if self.outofMissiles and ammo.Missiles>0 then
self.outoffMissiles=false
end
if ammo.Missiles==0 and self.ammo.Missiles>0 and not self.outofMissiles then
self.outofMissiles=true
self:OutOfMissiles()
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Status Info Common to Air, Land and Sea
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -5070,56 +5195,6 @@ function OPSGROUP:_MissileCategoryName(categorynumber)
return cat
end
--- Check if group got stuck.
-- @param #OPSGROUP self
function OPSGROUP:_CheckStuck()
-- Holding means we are not stuck.
if self:IsHolding() then
return
end
-- Current time.
local Tnow=timer.getTime()
-- Expected speed in m/s.
local ExpectedSpeed=self:GetExpectedSpeed()
-- Current speed in m/s.
local speed=self:GetVelocity()
-- Check speed.
if speed<0.5 then
if ExpectedSpeed>0 and not self.stuckTimestamp then
self:T2(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected", speed, ExpectedSpeed))
self.stuckTimestamp=Tnow
self.stuckVec3=self:GetVec3()
end
else
-- Moving (again).
self.stuckTimestamp=nil
end
-- Somehow we are not moving...
if self.stuckTimestamp then
-- Time we are holding.
local holdtime=Tnow-self.stuckTimestamp
if holdtime>=10*60 then
self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime))
--TODO: Stuck event!
end
end
end
--- Get coordinate from an object.
-- @param #OPSGROUP self
-- @param Wrapper.Object#OBJECT Object The object.