diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 6e9cba673..3d8a66ae1 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -458,27 +458,25 @@ end -- @param #string To To state. -- @param Core.Point#COORDINATE Coordinate Coordinate where to go. -- @param #number Speed Speed in knots. Default cruise speed. --- @param #number Formation Formation the group will use. --- @param #number ResumeRoute If true, resume route after detour point was reached. +-- @param #number Formation Formation of the group. +-- @param #number ResumeRoute If true, resume route after detour point was reached. If false, the group will stop at the detour point and wait for futher commands. function ARMYGROUP:onafterDetour(From, Event, To, Coordinate, Speed, Formation, ResumeRoute) - -- Waypoints. - local waypoints={} - - -- Get current speed in km/h. - local speed=Speed and UTILS.KnotsToKmph(Speed) or self.group:GetVelocityKMH() + -- Speed in knots. + Speed=Speed or self:GetSpeedCruise() - -- Current waypoint. - local current=self:GetCoordinate():WaypointGround(Speed, Formation) - table.insert(waypoints, current) + -- ID of current waypoint. + local uid=self:GetWaypointCurrent().uid - -- At each waypoint report passing. - local Task=self.group:TaskFunction("ARMYGROUP._DetourReached", self, ResumeRoute) + -- Add waypoint after current. + local wp=self:AddWaypoint(Coordinate, Speed, uid, Formation, true) - local detour=Coordinate:WaypointGround(Speed, Formation, {Task}) - table.insert(waypoints, detour) - - self:Route(waypoints) + -- Set if we want to resume route after reaching the detour waypoint. + if ResumeRoute then + wp.detour=1 + else + wp.detour=0 + end end @@ -491,25 +489,6 @@ function ARMYGROUP:onafterDetourReached(From, Event, To) self:I(self.lid.."Group reached detour coordinate.") end ---- Function called when a group is passing a waypoint. ---@param Wrapper.Group#GROUP group Group that passed the waypoint ---@param #ARMYGROUP armygroup Army group object. ---@param #boolean resume Resume route. -function ARMYGROUP._DetourReached(group, armygroup, resume) - - -- Debug message. - local text=string.format("Group reached detour coordinate") - armygroup:I(armygroup.lid..text) - - if resume then - local indx=armygroup:GetWaypointIndexNext(true) - local speed=armygroup:GetSpeedToWaypoint(indx) - armygroup:__UpdateRoute(-1, indx, speed, armygroup.formation) - end - - armygroup:DetourReached() - -end --- On after "FullStop" event. -- @param #ARMYGROUP self @@ -684,29 +663,36 @@ end --- Add an a waypoint to the route. -- @param #ARMYGROUP self --- @param Core.Point#COORDINATE coordinate The coordinate of the waypoint. Use COORDINATE:SetAltitude(altitude) to define the altitude. --- @param #number speed Speed in knots. Default is default cruise speed or 70% of max speed. --- @param #number wpnumber Waypoint number. Default at the end. --- @param #number formation Formation the group will use. --- @param #boolean updateroute If true or nil, call UpdateRoute. If false, no call. --- @return #number Waypoint index. -function ARMYGROUP:AddWaypoint(coordinate, speed, wpnumber, formation, updateroute) +-- @param Core.Point#COORDINATE Coordinate The coordinate of the waypoint. Use COORDINATE:SetAltitude(altitude) to define the altitude. +-- @param #number Speed Speed in knots. Default is default cruise speed or 70% of max speed. +-- @param #number AfterWaypointWithID Insert waypoint after waypoint given ID. Default is to insert as last waypoint. +-- @param #number Formation Formation the group will use. +-- @param #boolean Updateroute If true or nil, call UpdateRoute. If false, no call. +-- @return Ops.OpsGroup#OPSGROUP.Waypoint Waypoint table. +function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation, Updateroute) - -- Waypoint number. Default is at the end. - wpnumber=wpnumber or #self.waypoints+1 - + -- Set waypoint index. + local wpnumber=#self.waypoints+1 + if wpnumber then + local index=self:GetWaypointIndex(AfterWaypointWithID) + if index then + wpnumber=index+1 + end + end + + -- Check if final waypoint is still passed. if wpnumber>self.currentwp then self.passedfinalwp=false end -- Speed in knots. - speed=speed or self:GetSpeedCruise() + Speed=Speed or self:GetSpeedCruise() -- Speed at waypoint. - local speedkmh=UTILS.KnotsToKmph(speed) + local speedkmh=UTILS.KnotsToKmph(Speed) -- Create a Naval waypoint. - local wp=coordinate:WaypointGround(speedkmh, formation) + local wp=Coordinate:WaypointGround(speedkmh, Formation) -- Create waypoint data table. local waypoint=self:_CreateWaypoint(wp) @@ -715,15 +701,15 @@ function ARMYGROUP:AddWaypoint(coordinate, speed, wpnumber, formation, updaterou self:_AddWaypoint(waypoint, wpnumber) -- Debug info. - self:T(self.lid..string.format("Adding GROUND waypoint #%d, speed=%.1f knots. Last waypoint passed was #%s. Total waypoints #%d", wpnumber, speed, self.currentwp, #self.waypoints)) + self:T(self.lid..string.format("Adding GROUND waypoint #%d, speed=%.1f knots. Last waypoint passed was #%s. Total waypoints #%d", wpnumber, Speed, self.currentwp, #self.waypoints)) -- Update route. - if updateroute==nil or updateroute==true then + if Updateroute==nil or Updateroute==true then self:_CheckGroupDone(1) end - return wpnumber + return waypoint end --- Initialize group parameters. Also initializes waypoints if self.waypoints is nil. @@ -822,7 +808,7 @@ function ARMYGROUP:_InitGroup() --text=text..string.format("Radio = %.1f MHz %s %s\n", self.radioFreq, UTILS.GetModulationName(self.radioModu), tostring(self.radioOn)) --text=text..string.format("Ammo = %d (G=%d/R=%d/B=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Bombs, self.ammo.Missiles) text=text..string.format("FSM state = %s\n", self:GetState()) - text=text..string.format("Is alive = %s\n", tostring(self.group:IsAlive())) + 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) @@ -838,49 +824,6 @@ end -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Check if group is done, i.e. --- --- * passed the final waypoint, --- * no current task --- * no current mission --- * number of remaining tasks is zero --- * number of remaining missions is zero --- --- @param #ARMYGROUP self --- @param #number delay Delay in seconds. -function ARMYGROUP:_CheckGroupDone(delay) - - if self:IsAlive() and self.ai then - - if delay and delay>0 then - -- Delayed call. - self:ScheduleOnce(delay, ARMYGROUP._CheckGroupDone, self) - else - - if self.passedfinalwp then - - if #self.waypoints>1 and self.adinfinitum then - - local speed=self:GetSpeedToWaypoint(1) - - -- Start route at first waypoint. - self:__UpdateRoute(-1, 1, speed, self.formation) - - end - - else - - self:UpdateRoute(nil, nil, self.formation) - - end - - end - - end - -end - - --- Get default cruise speed. -- @param #ARMYGROUP self -- @return #number Cruise speed (>0) in knots. @@ -888,21 +831,6 @@ function ARMYGROUP:GetSpeedCruise() return UTILS.KmphToKnots(self.speedCruise or self.speedmax*0.7) end ---- Returns a non-zero speed to the next waypoint (even if the waypoint speed is zero). --- @param #ARMYGROUP self --- @param #number indx Waypoint index. --- @return #number Speed to next waypoint (>0) in knots. -function ARMYGROUP:GetSpeedToWaypoint(indx) - - local speed=self:GetWaypointSpeed(indx) - - if speed<=0.1 then - speed=self:GetSpeedCruise() - end - - return speed -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index caef3f605..940da0b03 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -1778,7 +1778,6 @@ function FLIGHTGROUP:_CheckGroupDone(delay) return end - -- Number of tasks remaining. local nTasks=self:CountRemainingTasks() diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 58aebfd15..fbd4fa740 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -93,6 +93,7 @@ function NAVYGROUP:New(GroupName) -- Defaults self:SetDefaultROE() + self:SetDefaultAlarmstate() self:SetDetection() self:SetPatrolAdInfinitum(true) @@ -418,11 +419,23 @@ function NAVYGROUP:onafterStatus(From, Event, To) local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks() local nMissions=self:CountRemainingMissison() - local intowind=self:IsSteamingIntoWind() and UTILS.SecondsToClock(self.intowind.Tstop-timer.getAbsTime(), true) or "N/A" + local intowind=self:IsSteamingIntoWind() and UTILS.SecondsToClock(self.intowind.Tstop-timer.getAbsTime(), true) or "N/A" + local turning=tostring(self:IsTurning()) + local alt=pos.y + local speedExpected=UTILS.MpsToKnots(self.speed or 0) + + local wpidxCurr=self.currentwp + local wpuidCurr=0 + local wpidxNext=self:GetWaypointIndexNext() + local wpuidNext=0 + local wpDist=UTILS.MetersToNM(self:GetDistanceToWaypoint()) + local wpETA=UTILS.SecondsToClock(self:GetTimeToWaypoint(), true) + local roe=self:GetROE() or 0 + local als=self:GetAlarmstate() or 0 -- Info text. - local text=string.format("%s (T=%d,M=%d): Wp=%d-->%d (of %d) Speed=%.1f (%.1f) Depth=%.1f Hdg=%03d Turn=%s Collision=%d IntoWind=%s ROE=%d AS=%d", - fsmstate, nTaskTot, nMissions, self.currentwp, self:GetWaypointIndexNext(), #self.waypoints, speed, UTILS.MpsToKnots(self.speed or 0), pos.y, hdg, tostring(self:IsTurning()), freepath, intowind, 0, 0) + local text=string.format("%s [ROE=%d,AS=%d, T/M=%d/%d]: Wp=%d[%d]-->%d[%d] (of %d) Dist=%.1f NM ETA=%s - Speed=%.1f (%.1f) kts, Depth=%.1f m, Hdg=%03d, Turn=%s Collision=%d IntoWind=%s", + fsmstate, roe, als, nTaskTot, nMissions, wpidxCurr, wpuidCurr, wpidxNext, wpuidNext, #self.waypoints, wpDist, wpETA, speed, speedExpected, alt, hdg, turning, freepath, intowind) self:I(self.lid..text) else @@ -536,16 +549,15 @@ function NAVYGROUP:onafterSpawned(From, Event, To) if self.ai then - -- Set default ROE and ROT options. - self:SetOptionROE(self.roe) + -- Set default ROE and Alarmstate options. + self:SetOptionROE(self.roe) + self:SetOptionAlarmstate(self.alarmstate) end -- Get orientation. self.Corientlast=self.group:GetUnit(1):GetOrientationX() - self.depth=self.group:GetHeight() - -- Update route. self:Cruise() @@ -562,7 +574,7 @@ end function NAVYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Depth) -- Update route from this waypoint number onwards. - n=n or self:GetWaypointIndexNext(self.adinfinitum) + n=n or self:GetWaypointIndexNext() -- Debug info. self:T(self.lid..string.format("FF Update route n=%d", n)) @@ -607,7 +619,7 @@ function NAVYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Depth) else - -- Dive depth is applied to all other waypoints. + -- Dive depth is applied to all other waypoints. if self.depth then wp.alt=-self.depth else @@ -637,10 +649,11 @@ function NAVYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Depth) else --- - -- No waypoints left + -- No waypoints left ==> Full Stop --- - self:I(self.lid..string.format("No waypoints left")) + self:E(self.lid..string.format("WARNING: No waypoints left ==> Full Stop!")) + self:FullStop() end @@ -656,20 +669,20 @@ end -- @param #number Depth Depth in meters. Default 0 meters. -- @param #number ResumeRoute If true, resume route after detour point was reached. If false, the group will stop at the detour point and wait for futher commands. function NAVYGROUP:onafterDetour(From, Event, To, Coordinate, Speed, Depth, ResumeRoute) - - -- Waypoints. - local waypoints={} -- Depth for submarines. - local depth=Depth or 0 + Depth=Depth or 0 -- Speed in knots. Speed=Speed or self:GetSpeedCruise() - local wpindx=self.currentwp+1 + -- ID of current waypoint. + local uid=self:GetWaypointCurrent().uid - local wp=self:AddWaypoint(Coordinate, Speed, wpindx, true) + -- Add waypoint after current. + local wp=self:AddWaypoint(Coordinate, Speed, uid, Depth, true) + -- Set if we want to resume route after reaching the detour waypoint. if ResumeRoute then wp.detour=1 else @@ -687,26 +700,6 @@ function NAVYGROUP:onafterDetourReached(From, Event, To) self:I(self.lid.."Group reached detour coordinate.") end ---- Function called when a group is passing a waypoint. ---@param Wrapper.Group#GROUP group Group that passed the waypoint ---@param #NAVYGROUP navygroup Navy group object. ---@param #boolean resume Resume route. -function NAVYGROUP._DetourReached(group, navygroup, resume) - - -- Debug message. - local text=string.format("Group reached detour coordinate") - navygroup:I(navygroup.lid..text) - - if resume then - local indx=navygroup:GetWaypointIndexNext(true) - local speed=navygroup:GetSpeedToWaypoint(indx) - navygroup:UpdateRoute(indx, speed, navygroup.depth) - end - - navygroup:DetourReached() - -end - --- On after "TurnIntoWind" event. -- @param #NAVYGROUP self -- @param #string From From state. @@ -739,10 +732,11 @@ function NAVYGROUP:onafterTurnIntoWind(From, Event, To, IntoWind) local coord=self:GetCoordinate() local Coord=coord:Translate(distance, IntoWind.Heading) + + -- ID of current waypoint. + local uid=self:GetWaypointCurrent().uid - local wpindex=self.currentwp+1 --self:GetWaypointIndexNext(false) - - local wptiw=self:AddWaypoint(Coord, speed, wpindex, true) + local wptiw=self:AddWaypoint(Coord, speed, uid) wptiw.intowind=true IntoWind.waypoint=wptiw @@ -751,14 +745,6 @@ function NAVYGROUP:onafterTurnIntoWind(From, Event, To, IntoWind) IntoWind.Coordinate:MarkToAll("Return coord") end - --[[ - local wp={} - wp[1]=coord:WaypointNaval(UTILS.KnotsToKmph(speed)) - wp[2]=Coord:WaypointNaval(UTILS.KnotsToKmph(speed)) - - self:Route(wp) - ]] - end --- On after "TurnIntoWindOver" event. @@ -816,10 +802,10 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #number Speed Speed in knots until next waypoint is reached. +-- @param #number Speed Speed in knots until next waypoint is reached. Default is speed set for waypoint. function NAVYGROUP:onafterCruise(From, Event, To, Speed) - -- + -- No set depth. self.depth=nil self:__UpdateRoute(-1, nil, Speed) @@ -1001,28 +987,36 @@ end --- Add an a waypoint to the route. -- @param #NAVYGROUP self --- @param Core.Point#COORDINATE coordinate The coordinate of the waypoint. Use COORDINATE:SetAltitude(altitude) to define the altitude. --- @param #number speed Speed in knots. Default is default cruise speed or 70% of max speed. --- @param #number wpnumber Waypoint number. Default at the end. --- @param #boolean updateroute If true or nil, call UpdateRoute. If false, no call. +-- @param Core.Point#COORDINATE Coordinate The coordinate of the waypoint. Use COORDINATE:SetAltitude(altitude) to define the altitude. +-- @param #number Speed Speed in knots. Default is default cruise speed or 70% of max speed. +-- @param #number AfterWaypointWithID Insert waypoint after waypoint given ID. Default is to insert as last waypoint. +-- @param #number Depth Depth at waypoint in meters. +-- @param #boolean Updateroute If true or nil, call UpdateRoute. If false, no call. -- @return Ops.OpsGroup#OPSGROUP.Waypoint Waypoint table. -function NAVYGROUP:AddWaypoint(coordinate, speed, wpnumber, updateroute) - - -- Waypoint number. Default is at the end. - wpnumber=wpnumber or #self.waypoints+1 +function NAVYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Updateroute) + -- Set waypoint index. + local wpnumber=#self.waypoints+1 + if wpnumber then + local index=self:GetWaypointIndex(AfterWaypointWithID) + if index then + wpnumber=index+1 + end + end + + -- Check if final waypoint is still passed. if wpnumber>self.currentwp then self.passedfinalwp=false end -- Speed in knots. - speed=speed or self:GetSpeedCruise() + Speed=Speed or self:GetSpeedCruise() -- Speed at waypoint. - local speedkmh=UTILS.KnotsToKmph(speed) + local speedkmh=UTILS.KnotsToKmph(Speed) -- Create a Naval waypoint. - local wp=coordinate:WaypointNaval(speedkmh) + local wp=Coordinate:WaypointNaval(speedkmh) -- Create waypoint data table. local waypoint=self:_CreateWaypoint(wp) @@ -1031,10 +1025,10 @@ function NAVYGROUP:AddWaypoint(coordinate, speed, wpnumber, updateroute) self:_AddWaypoint(waypoint, wpnumber) -- Debug info. - self:I(self.lid..string.format("Adding NAVAL waypoint index=%d uid=%d, speed=%.1f knots. Last waypoint passed was #%d. Total waypoints #%d", wpnumber, waypoint.uid, speed, self.currentwp, #self.waypoints)) + self:I(self.lid..string.format("Adding NAVAL waypoint index=%d uid=%d, speed=%.1f knots. Last waypoint passed was #%d. Total waypoints #%d", wpnumber, waypoint.uid, Speed, self.currentwp, #self.waypoints)) -- Update route. - if updateroute==nil or updateroute==true then + if Updateroute==nil or Updateroute==true then self:_CheckGroupDone(1) end @@ -1076,8 +1070,8 @@ function NAVYGROUP:_InitGroup() -- Max speed in km/h. self.speedmax=self.group:GetSpeedMax() - -- Cruise speed: 70% of max speed but within limit. - --self.speedCruise=self.speedmax*0.7 + -- Cruise speed: 70% of max speed. + self.speedCruise=self.speedmax*0.7 -- Group ammo. --self.ammo=self:GetAmmoTot() @@ -1106,6 +1100,7 @@ function NAVYGROUP:_InitGroup() end end + -- Get all units of the group. local units=self.group:GetUnits() for _,_unit in pairs(units) do @@ -1351,58 +1346,18 @@ function NAVYGROUP:_CheckStuck() local ExpectedSpeed=self:GetExpectedSpeed() - local speed=self.group:GetVelocityMPS() + local speed=self:GetVelocity() if speed<0.5 and ExpectedSpeed>0 then if not self.holdtimestamp then - self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f expected=%.1f knots", speed, ExpectedSpeed)) + self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected", speed, ExpectedSpeed)) self.holdtimestamp=timer.getTime() end end end ---- Check if group is done, i.e. --- --- * passed the final waypoint, --- * no current task --- * no current mission --- * number of remaining tasks is zero --- * number of remaining missions is zero --- --- @param #NAVYGROUP self --- @param #number delay Delay in seconds. -function NAVYGROUP:_CheckGroupDone(delay) - if self:IsAlive() and self.ai then - - if delay and delay>0 then - -- Delayed call. - self:ScheduleOnce(delay, NAVYGROUP._CheckGroupDone, self) - else - - if self.passedfinalwp then - - if #self.waypoints>1 and self.adinfinitum then - - local speed=self:GetSpeedToWaypoint(1) - - -- Start route at first waypoint. - self:__UpdateRoute(-1, 1, speed, self.depth) - - end - - else - - self:__UpdateRoute(-1, nil, nil, self.depth) - - end - - end - - end - -end --- Check queued turns into wind. -- @param #NAVYGROUP self @@ -1466,33 +1421,7 @@ function NAVYGROUP:GetSpeedCruise() return UTILS.KmphToKnots(self.speedCruise or self.speedmax*0.7) end ---- Returns a non-zero speed to the next waypoint (even if the waypoint speed is zero). --- @param #NAVYGROUP self --- @param #number indx Waypoint index. --- @return #number Speed to next waypoint (>0) in knots. -function NAVYGROUP:GetSpeedToWaypoint(indx) - local speed=self:GetWaypointSpeed(indx) - - if speed<=0.1 then - speed=self:GetSpeedCruise() - end - - return speed -end - ---- Returns the currently expected speed. --- @param #NAVYGROUP self --- @return #number Expected speed in m/s. -function NAVYGROUP:GetExpectedSpeed() - - if self:IsHolding() then - return 0 - else - return self.speed or 0 - end - -end --- Check queued turns into wind. -- @param #NAVYGROUP self @@ -1602,15 +1531,23 @@ function NAVYGROUP:_FindPathToNextWaypoint() if path then -- Loop over nodes in found path. + local uid=self:GetWaypointCurrent().uid -- ID of current waypoint. + for i,_node in ipairs(path) do local node=_node --Core.Astar#ASTAR.Node -- Waypoint index. local wpindex=self:GetWaypointIndexCurrent()+i + + -- ID of current waypoint. + local uid=self:GetWaypointCurrent().uid -- Add waypoints along detour path to next waypoint. - local wp=self:AddWaypoint(node.coordinate, speed, wpindex) + local wp=self:AddWaypoint(node.coordinate, speed, uid) wp.astar=true + + -- Update id so the next wp is added after this one. + uid=wp.uid -- Debug: smoke and mark path. node.coordinate:MarkToAll(string.format("Path node #%d", i)) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index ee8c0bc1c..9587aa307 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -95,9 +95,10 @@ -- -- # The OPSGROUP Concept -- --- The OPSGROUP class contains common functions used by other classes such as FLIGHGROUP, NAVYGROUP. +-- The OPSGROUP class contains common functions used by other classes such as FLIGHGROUP, NAVYGROUP and ARMYGROUP. +-- Those classes inherit everything of this class and extend it with features specific to their unit category. -- --- This class is **not** meant to be used itself by the end user. +-- This class is **NOT** meant to be used by the end user itself. -- -- -- @field #OPSGROUP @@ -229,7 +230,7 @@ OPSGROUP.TaskType={ --- NavyGroup version. -- @field #string version -OPSGROUP.version="0.1.0" +OPSGROUP.version="0.2.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -431,6 +432,18 @@ function OPSGROUP:GetCoordinate() return nil end +--- Get current velocity of the group. +-- @param #OPSGROUP self +-- @return #number Velocity in m/s. +function OPSGROUP:GetVelocity() + if self:IsAlive()~=nil then + return self.group:GetVelocityMPS() + else + self:E(self.lid.."WARNING: Group is not alive. Cannot get velocity!") + end + return nil +end + --- Get current heading of the group. -- @param #OPSGROUP self -- @return #number Current heading of the group in degrees. @@ -690,8 +703,9 @@ end --- Get coordinate of next waypoint of the group. -- @param #OPSGROUP self +-- @param #boolean cyclic If true, return first waypoint if last waypoint was reached. -- @return Core.Point#COORDINATE Coordinate of the next waypoint. -function OPSGROUP:GetNextWaypointCoordinate() +function OPSGROUP:GetNextWaypointCoordinate(cyclic) -- Get next waypoint local waypoint=self:GetWaypointNext(cyclic) @@ -726,6 +740,98 @@ function OPSGROUP:GetWaypointSpeed(indx) return nil end +--- Get unique ID of waypoint. +-- @param #OPSGROUP self +-- @param #number indx Waypoint index. +-- @return #number Unique ID. +function OPSGROUP:GetWaypointID(indx) + + local waypoint=self:GetWaypoint(indx) + + if waypoint then + return waypoint.uid + end + + return nil + +end + +--- Returns a non-zero speed to the next waypoint (even if the waypoint speed is zero). +-- @param #OPSGROUP self +-- @param #number indx Waypoint index. +-- @return #number Speed to next waypoint (>0) in knots. +function OPSGROUP:GetSpeedToWaypoint(indx) + + local speed=self:GetWaypointSpeed(indx) + + if speed<=0.1 then + speed=self:GetSpeedCruise() + end + + return speed +end + +--- Get distance to waypoint. +-- @param #OPSGROUP self +-- @param #number indx Waypoint index. Default is the next waypoint. +-- @return #number Distance in meters. +function OPSGROUP:GetDistanceToWaypoint(indx) + local dist=0 + + if #self.waypoints>0 then + + indx=indx or self:GetWaypointIndexNext() + + local wp=self:GetWaypoint(indx) + + if wp then + + local coord=self:GetCoordinate() + + dist=coord:Get2DDistance(wp.coordinate) + end + + end + + return dist +end + +--- Get time to waypoint based on current velocity. +-- @param #OPSGROUP self +-- @param #number indx Waypoint index. Default is the next waypoint. +-- @return #number Time in seconds. If velocity is 0 +function OPSGROUP:GetTimeToWaypoint(indx) + + local s=self:GetDistanceToWaypoint(indx) + + local v=self:GetVelocity() + + local t=s/v + + if t==math.inf then + return 365*24*60*60 + elseif t==math.nan then + return 0 + else + return t + end + +end + +--- Returns the currently expected speed. +-- @param #OPSGROUP self +-- @return #number Expected speed in m/s. +function OPSGROUP:GetExpectedSpeed() + + if self:IsHolding() then + return 0 + else + return self.speed or 0 + end + +end + + --- Remove a waypoint with a ceratin UID. -- @param #OPSGROUP self @@ -765,7 +871,9 @@ function OPSGROUP:RemoveWaypoint(wpindex) -- Waypoint was not reached yet. if wpindex > self.currentwp then - -- Could be that we just removed the only remaining waypoint ==> passedfinalwp=true. + --- + -- Removed a FUTURE waypoint + --- -- TODO: patrol adinfinitum. @@ -773,16 +881,16 @@ function OPSGROUP:RemoveWaypoint(wpindex) self.passedfinalwp=true end - env.info("FF passed final waypoint after remove current wp = "..self.currentwp) + env.info("FF passed final waypoint after remove! current wp = "..self.currentwp) self:_CheckGroupDone(1) - - --elseif wpindex==self.currentwp then - - -- Removed the waypoint we just passed. - + else + --- + -- Removed a waypoint ALREADY PASSED + --- + -- If an already passed waypoint was deleted, we do not need to update the route. -- If current wp = 1 it stays 1. Otherwise decrease current wp. @@ -962,20 +1070,17 @@ end --- Add a *waypoint* task. -- @param #OPSGROUP self -- @param #table task DCS task table structure. --- @param #number waypointindex Number of waypoint. Counting starts at one! Default is the as *next* waypoint. +-- @param #OPSGROUP.Waypoint Waypoint where the task is executed. Default is the at *next* waypoint. -- @param #string description Brief text describing the task, e.g. "Attack SAM". -- @param #number prio Priority of the task. Number between 1 and 100. Default is 50. -- @param #number duration Duration before task is cancelled in seconds counted after task started. Default never. -- @return #OPSGROUP.Task The task structure. -function OPSGROUP:AddTaskWaypoint(task, waypointindex, description, prio, duration) - - -- Index. - waypointindex=waypointindex or (self.currentwp and self.currentwp+1 or 2) +function OPSGROUP:AddTaskWaypoint(task, Waypoint, description, prio, duration) - -- Get waypoint - local waypoint=self:GetWaypointByIndex(waypointindex) - - if waypoint then + -- Waypoint of task. + Waypoint=Waypoint or self:GetWaypointNext() + + if Waypoint then -- Increase counter. self.taskcounter=self.taskcounter+1 @@ -989,7 +1094,7 @@ function OPSGROUP:AddTaskWaypoint(task, waypointindex, description, prio, durati newtask.id=self.taskcounter newtask.duration=duration newtask.time=0 - newtask.waypoint=waypoint.uid + newtask.waypoint=Waypoint.uid newtask.type=OPSGROUP.TaskType.WAYPOINT newtask.stopflag=USERFLAG:New(string.format("%s StopTaskFlag %d", self.groupname, newtask.id)) newtask.stopflag:Set(0) @@ -1002,10 +1107,9 @@ function OPSGROUP:AddTaskWaypoint(task, waypointindex, description, prio, durati self:T3({newtask=newtask}) -- Update route. - --self:_CheckGroupDone(1) self:__UpdateRoute(-1) - return newtask + return newtask end return nil @@ -1807,8 +1911,8 @@ function OPSGROUP:RouteToMission(mission, delay) self:ScheduleOnce(delay, OPSGROUP.RouteToMission, self, mission) else - -- Next waypoint. - local nextwaypoint=self.currentwp+1 + -- ID of current waypoint. + local uid=self:GetWaypointCurrent().uid -- Get coordinate where the mission is executed. local waypointcoord=mission:GetMissionWaypointCoord(self.group) @@ -1817,9 +1921,12 @@ function OPSGROUP:RouteToMission(mission, delay) for _,task in pairs(mission.enrouteTasks) do self:AddTaskEnroute(task) end + + -- Speed to mission waypoint. + local SpeedToMission=UTILS.KmphToKnots(self.speedCruise) -- Add waypoint. - local waypoint=self:AddWaypoint(waypointcoord, UTILS.KmphToKnots(self.speedCruise), nextwaypoint, false) + local waypoint=self:AddWaypoint(waypointcoord, SpeedToMission, nil, false) -- Special for Troop transport. if mission.type==AUFTRAG.Type.TROOPTRANSPORT then @@ -1841,7 +1948,7 @@ function OPSGROUP:RouteToMission(mission, delay) end -- Add waypoint task. UpdateRoute is called inside. - local waypointtask=self:AddTaskWaypoint(mission.DCStask, nextwaypoint, mission.name, mission.prio, mission.duration) + local waypointtask=self:AddTaskWaypoint(mission.DCStask, waypoint, mission.name, mission.prio, mission.duration) -- Set waypoint task. mission:SetGroupWaypointTask(self, waypointtask) @@ -2126,6 +2233,10 @@ function OPSGROUP:onafterCheckZone(From, Event, To) end end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Internal Check Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --- Check if group is in zones. -- @param #OPSGROUP self function OPSGROUP:_CheckInZones() @@ -2248,6 +2359,106 @@ function OPSGROUP:_CheckDetectedUnits() end +--- Check if passed the final waypoint and, if necessary, update route. +-- @param #OPSGROUP self +-- @param #number delay Delay in seconds. +function OPSGROUP:_CheckGroupDone(delay) + + if self:IsAlive() and self.ai then + + if delay and delay>0 then + -- Delayed call. + self:ScheduleOnce(delay, self._CheckGroupDone, self) + else + + if self.passedfinalwp then + + --- + -- Passed FINAL waypoint + --- + + if #self.waypoints>1 then + + if self.adinfinitum then + + -- Get positive speed to first waypoint. + local speed=self:GetSpeedToWaypoint(1) + + -- Start route at first waypoint. + self:__UpdateRoute(-1, 1, speed) + + self:I(self.lid..string.format("Passed final WP, #WP>1, adinfinitum=TRUE ==> Goto WP 1 at speed>0")) + + self.passedfinalwp=false + + else + -- No further waypoints. Command a full stop. + self:__FullStop(-1) + + self:I(self.lid..string.format("Passed final WP, #WP>1, adinfinitum=FALSE ==> Full Stop")) + end + + elseif #self.waypoints==1 then + + --- Only one WP left + + -- The last waypoint. + local waypoint=self.waypoints[1] --Ops.OpsGroup#OPSGROUP.Waypoint + + local dist=self:GetCoordinate():Get2DDistance(waypoint.coordinate) + + + if self.adinfinitum and dist>1000 then -- Note that dist>100 caused the same wp to be passed a lot of times. + + self:I(self.lid..string.format("Passed final WP, #WP=1, adinfinitum=TRUE dist>1000 ==> Goto WP 1 at speed>0")) + + -- Get positive speed to first waypoint. + local speed=self:GetSpeedToWaypoint(1) + + -- Start route at first waypoint. + self:__UpdateRoute(-1, 1, speed) + + self.passedfinalwp=false + + else + + self:I(self.lid..string.format("Passed final WP, #WP=1, adinfinitum=FALSE or dist<1000 ==> Full Stop")) + + self:__FullStop(-1) + + end + + else + + --- No waypoints left + + -- No further waypoints. Command a full stop. + self:__FullStop(-1) + + end + + else + + --- + -- Final waypoint NOT passed yet + --- + + if #self.waypoints>0 then + self:I(self.lid..string.format("NOT Passed final WP, #WP>0 ==> Update Route")) + self:__UpdateRoute(-1) + else + self:E(self.lid..string.format("WARNING: No waypoints left! Commanding a Full Stop")) + self:__FullStop(-1) + end + + end + + end + + end + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Waypoints & Routing ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2261,11 +2472,11 @@ function OPSGROUP:_CreateWaypoint(waypoint, formation, detour) waypoint.uid=self.wpcounter waypoint.coordinate=COORDINATE:New(waypoint.x, waypoint.alt, waypoint.y) waypoint.detour=detour and detour or false - waypoint.formation=formation if formation then waypoint.action=formation end - waypoint.onroad=onroad and onroad or false + waypoint.npassed=0 + waypoint.patrol=false self.wpcounter=self.wpcounter+1 @@ -2412,6 +2623,9 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid) -- Get the current waypoint index. opsgroup.currentwp=opsgroup:GetWaypointIndex(uid) + -- Increase passing counter. + waypoint.npassed=waypoint.npassed+1 + -- Set expected speed and formation from the next WP. local wpnext=opsgroup:GetWaypointNext() if wpnext then