From 459ff26868c4c1b33ed2fd19f330ad63dfcf014f Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 19 Oct 2020 23:33:39 +0200 Subject: [PATCH] Ops --- Moose Development/Moose/Core/Fsm.lua | 4 +- Moose Development/Moose/Ops/ArmyGroup.lua | 88 +++++++--- Moose Development/Moose/Ops/NavyGroup.lua | 81 ++++++---- Moose Development/Moose/Ops/OpsGroup.lua | 189 +++++++++++++++------- 4 files changed, 245 insertions(+), 117 deletions(-) diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 80dfcb0c5..0b49c3449 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -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 diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index a366d7a79..df52c7cc8 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -101,6 +101,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 - - local rearmed=self:_CheckAmmoFull() - - if rearmed then - self:Rearmed() - end - - else + -- Check ammo status. + self:_CheckAmmoStatus() - -- Update position etc. - self:_UpdatePosition() + -- 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) diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index dd0fb65a9..515aaf817 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -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) + + -- Get Descriptors. + self.descriptors=self.descriptors or unit:GetDesc() - self:GetAmmoUnit(unit, false) + -- Set type name. + self.actype=self.actype or unit:GetTypeName() - if unit:IsAlive() then + 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 diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 7a9828e81..d9a889e0a 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -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. @@ -3362,6 +3369,8 @@ function OPSGROUP:_CheckGroupDone(delay) -- Get current waypoint. local waypoint=self:GetWaypoint(self.currentwp) + + --env.info("FF CheckGroupDone") if waypoint then @@ -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.