From 657e3578e088f8d63f4e6b501b4f1ab495662356 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 25 Aug 2019 22:00:42 +0200 Subject: [PATCH] FG --- Moose Development/Moose/Ops/FlightGroup.lua | 200 +++++++++++++++--- .../Moose/Wrapper/Controllable.lua | 18 +- 2 files changed, 179 insertions(+), 39 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 9eaa21d60..8ae1debf2 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -52,8 +52,8 @@ FLIGHTGROUP = { flightgroup = nil, grouptemplate = nil, type = nil, - waypoints = nil, - coordinates = nil, + waypoints = {}, + coordinates = {}, element = {}, taskqueue = {}, taskcounter = nil, @@ -95,7 +95,7 @@ FLIGHTGROUP.ElementStatus={ -- @field #string skill Skill level. -- @field Wrapper.Unit#UNIT unit Element unit object. -- @field Wrapper.Group#GROUP group Group object of the element. --- @field #string status Status, i.e. born, parking, taxiing. +-- @field #string status Status, i.e. born, parking, taxiing. See @{#FLIGHTGROUP.ElementStatus}. --- Flight group tasks. -- @type FLIGHTGROUP.Mission @@ -136,6 +136,8 @@ FLIGHTGROUP.TaskStatus={ -- @field #table dcstask DCS task structure. -- @field #string description Brief text which describes the task. -- @field #string status Task status. +-- @field #number duration Duration before task is cancelled in seconds. Default never. +-- @field #number timestamp Abs. mission time, when task was started. --- FLIGHTGROUP class version. @@ -298,15 +300,16 @@ end -- @param #string description Brief text describing the task, e.g. "Attack SAM". -- @param #table task DCS task table stucture. -- @param #number prio Priority of the task. --- @param #string clock Mission time when task is executed. Default in 60 seconds. If argument passed as #number, it defines a relative delay in seconds. +-- @param #string clock Mission time when task is executed. Default in 5 seconds. If argument passed as #number, it defines a relative delay in seconds. +-- @param #number duration Duration before task is cancelled in seconds counted after task started. Default never. -- @return #FLIGHTGROUP self -function FLIGHTGROUP:AddTask(description, task, prio, clock) +function FLIGHTGROUP:AddTask(description, task, prio, clock, duration) -- Increase coutner. self.taskcounter=self.taskcounter+1 -- Set time. - local time=timer.getAbsTime()+60 + local time=timer.getAbsTime()+5 if clock then if type(clock)=="string" then time=UTILS.ClockToSeconds(clock) @@ -323,6 +326,7 @@ function FLIGHTGROUP:AddTask(description, task, prio, clock) newtask.prio=prio or 50 newtask.time=time newtask.id=self.taskcounter + newtask.duration=duration self:T2({newtask=newtask}) @@ -428,7 +432,7 @@ function FLIGHTGROUP:onafterStart(From, Event, To) self:__FlightStatus(-1) end ---- On after Start event. Starts the FLIGHTGROUP FSM and event handlers. +--- On after "FlightSpawned" event. Sets the template, initializes the waypoints. -- @param #FLIGHTGROUP self -- @param Wrapper.Group#GROUP Group Flight group. -- @param #string From From state. @@ -437,10 +441,27 @@ end -- @param Wrapper.Group#GROUP Group The group object. function FLIGHTGROUP:onafterFlightSpawned(From, Event, To, Group) + -- Get template of group. self.template=self.flightgroup:GetTemplate() -- Init waypoints. self:_InitWaypoints() + + -- Route flight group but now with passing waypoint tasks. + self.flightgroup:Route(self.waypoints) +end + +--- On after "FlightAirborne" event. +-- @param #FLIGHTGROUP self +-- @param Wrapper.Group#GROUP Group Flight group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Group#GROUP Group The group object. +function FLIGHTGROUP:onafterFlightAirborne(From, Event, To, Group) + + -- Update + self:__QueueUpdate(-1) end @@ -456,7 +477,7 @@ function FLIGHTGROUP:onafterFlightStatus(From, Event, To) local fsmstate=self:GetState() -- Short info. - local text=string.format("Flight group status %s. Task=%d", fsmstate, self.taskcurrent) + local text=string.format("Flight status %s. Task=%d. Waypoint=%d/%d.", fsmstate, self.taskcurrent, self.currentwp or 0, #self.waypoints or 0) self:I(self.sid..text) -- Element status. @@ -483,18 +504,25 @@ function FLIGHTGROUP:onafterFlightStatus(From, Event, To) self:I(text) -- Task queue. - text="Tasks:" + text=string.format("Tasks #%d", #self.taskqueue) for i,_task in pairs(self.taskqueue) do local task=_task --#FLIGHTGROUP.Task local name=task.description local status=task.status local clock=UTILS.SecondsToClock(task.time) local eta=task.time-timer.getAbsTime() + local started=UTILS.SecondsToClock(task.timestamp or 0) + local duration=-1 + if task.duration then + duration=task.duration + if task.timestamp then + duration=task.duration-(timer.getAbsTime()-task.timestamp) + else + duration=task.duration + end + end -- Output text for element. - text=text..string.format("\n[%d] %s: status=%s, scheduled at %s (ETA %d seconds)", i, name, status, clock, eta) - end - if #self.taskqueue==0 then - text=text.." none!" + text=text..string.format("\n[%d] %s: status=%s, scheduled=%s (%d sec), started=%s, duration=%s", i, name, status, clock, eta, started, duration) end self:I(text) @@ -521,10 +549,30 @@ function FLIGHTGROUP:onafterQueueUpdate(From, Event, To) if task then self:TaskExecute(task) end + + else + + -- Get current task. + local task=self:GetTaskCurrent() + + -- Check if task has a defined duration. + if task and task.duration and task.timestamp then + + -- Check if max task duration is over. + local cancel=timer.getAbsTime()>task.timestamp+task.duration + + --self:E(string.format("FF timestap=%d , duration=%d, time=%d, stamp+duration=%s < time = %s", task.timestamp, task.duration, timer.getAbsTime(), task.timestamp+task.duration, tostring(cancel))) + + -- Cancel task if task is running longer than duration. + if cancel then + self:TaskCancel() + end + end end - self:__QueueUpdate(-1) + -- Update queue every ~5 sec. + self:__QueueUpdate(-5) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -773,6 +821,21 @@ function FLIGHTGROUP:onafterElementArrived(From, Event, To, Element) end +--- On after "PassingWaypoint" event. +-- @param #FLIGHTGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #number n Waypoint number passed. +-- @param #number N Final waypoint number. +function FLIGHTGROUP:onafterPassingWaypoint(From, Event, To, n, N) + local text=string.format("Group %s passed waypoint %d/%d", self.groupname, n, N) + self:I(self.sid..text) + MESSAGE:New(text, 30, "DEBUG"):ToAllIf(self.Debug) + +end + + --- On after "RTB" event. Send flightgroup back to base. -- @param #FLIGHTGROUP self -- @param #string From From state. @@ -830,17 +893,19 @@ function FLIGHTGROUP:onafterTaskExecute(From, Event, To, Task) -- Task done. local TaskDone=self.flightgroup:TaskFunction("FLIGHTGROUP._TaskDone", self, Task) - local TaskOrbit=self.flightgroup:TaskOrbit(Coord, 10000) -- Combo task. - local TaskCombo=self.flightgroup:TaskCombo({Task.dcstask, TaskDone, TaskOrbit}) - - -- Set task for group. - self.flightgroup:SetTask(TaskCombo, 1) + local TaskCombo=self.flightgroup:TaskCombo({Task.dcstask, TaskDone}) + + -- Set time stamp. + Task.timestamp=timer.getAbsTime() -- Task status executing. Task.status=FLIGHTGROUP.TaskStatus.EXECUTING + -- Set task for group. + self.flightgroup:SetTask(TaskCombo) + end @@ -864,6 +929,32 @@ function FLIGHTGROUP:onafterTaskPause(From, Event, To, Task) end +--- On after TaskCancel event. +-- @param #FLIGHTGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #FLIGHTGROUP.Task Task +function FLIGHTGROUP:onafterTaskCancel(From, Event, To) + + -- Clear tasks. + self.flightgroup:ClearTasks() + + -- Get current task. + local task=self:GetTaskCurrent() + + if task then + local text=string.format("Task %s ID=%d cancelled.", task.description, task.id) + MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug) + self:I(self.sid..text) + self:TaskDone(task) + else + local text=string.format("WARNING: No current task to cancel!") + MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug) + self:I(self.sid..text) + end +end + --- On after TaskDone event. -- @param #FLIGHTGROUP self @@ -874,12 +965,19 @@ end function FLIGHTGROUP:onafterTaskDone(From, Event, To, Task) -- Debug message. - local text=string.format("Task %s ID=%d done.", Task.description, Task.id) + local text=string.format("Task done: %s ID=%d", Task.description, Task.id) MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug) self:I(self.sid..text) + -- No current task. self.taskcurrent=0 + + -- Task status done. Task.status=FLIGHTGROUP.TaskStatus.ACCOMPLISHED + + -- Update route. + self:_UpdateRoute() + end @@ -913,7 +1011,7 @@ end -- Task functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Sort task queue. +--- Get next task in queue. Task needs to be in state SCHEDULED and time must have passed. -- @param #FLIGHTGROUP self function FLIGHTGROUP:GetTask() @@ -935,7 +1033,7 @@ function FLIGHTGROUP:GetTask() -- Look for first task that is not accomplished. for _,_task in pairs(self.taskqueue) do local task=_task --#FLIGHTGROUP.Task - if task.status~=FLIGHTGROUP.TaskStatus.ACCOMPLISHED and time>=task.time then + if task.status==FLIGHTGROUP.TaskStatus.SCHEDULED and time>=task.time then return task end end @@ -1129,6 +1227,13 @@ function FLIGHTGROUP:GetElementByName(unitname) return nil end +--- Get the currently executed task if there is any. +-- @param #FLIGHTGROUP self +-- @return #FLIGHTGROUP.Task Current task or nil. +function FLIGHTGROUP:GetTaskCurrent() + return self:GetTaskByID(self.taskcurrent, FLIGHTGROUP.TaskStatus.EXECUTING) +end + --- Get task by its id. -- @param #FLIGHTGROUP self -- @param #number id Task id. @@ -1178,18 +1283,41 @@ end --- Update route of group, e.g after new waypoints and/or waypoint tasks have been added. -- @param #FLIGHTGROUP self +-- @param #number n Number of next waypoint. Default self.currentwp+1. -- @return #FLIGHTGROUP self -function FLIGHTGROUP:_UpdateRoute() +function FLIGHTGROUP:_UpdateRoute(n) + + -- TODO: what happens if currentwp=#waypoints + n=n or self.currentwp+1 local wp={} - for i=self.currentwp,#self.waypoints do + -- Set "remaining" waypoits. + for i=n, #self.waypoints do table.insert(wp, self.waypoints[i]) end + + if #wp>0 then - self.flightgroup:GetTaskRoute() - self.flightgroup:TaskRoute(wp) - self.flightgroup:Route(wp, 1) + -- Route group to all defined waypoints remaining. + self.flightgroup:Route(wp, 1) + + else + + -- Get destination or home airbase. + local airbase=self.destination or self.homebase + + if airbase then + -- Route flight home. + self:RTB(airbase) + else + -- Let flight orbit. + self:Orbit() + end + + end + + return self end --- Initialize Mission Editor waypoints. @@ -1199,9 +1327,17 @@ function FLIGHTGROUP:_InitWaypoints() -- Waypoints of group as defined in the ME. self.waypoints=self.flightgroup:GetTemplateRoutePoints() + + for i,wp in pairs(self.waypoints) do + local Task=self.flightgroup:TaskFunction("FLIGHTGROUP._PassingWaypoint", self, i) + self.flightgroup:SetTaskWaypoint(wp, Task) + self:I({wptask=Task}) + end -- Init array. self.coordinates={} + + self:I(self.sid..string.format("FF number of waypoints = %d", #self.waypoints)) -- Set waypoint table. for i,point in ipairs(self.waypoints or {}) do @@ -1222,6 +1358,7 @@ function FLIGHTGROUP:_InitWaypoints() end + -- Set current waypoint. Counting starts a one. self.currentwp=1 return self @@ -1231,8 +1368,9 @@ end --@param Wrapper.Group#GROUP group Group that passed the waypoint --@param #FLIGHTGROUP flightgroup Flightgroup object. --@param #number i Waypoint number that has been reached. ---@param #number final Final waypoint number. -function FLIGHGROUP._PassingWaypoint(group, flightgroup, i, final) +function FLIGHTGROUP._PassingWaypoint(group, flightgroup, i) + + local final=#flightgroup.waypoints or 1 -- Debug message. local text=string.format("Group %s passing waypoint %d of %d.", group:GetName(), i, final) @@ -1246,13 +1384,13 @@ function FLIGHGROUP._PassingWaypoint(group, flightgroup, i, final) -- Debug message. MESSAGE:New(text,10):ToAllIf(flightgroup.Debug) - flightgroup:T(flightgroup.sid..text) + flightgroup:I(flightgroup.sid..text) -- Set current waypoint. flightgroup.currentwp=i -- Passing Waypoint event. - flightgroup:PassingWaypoint(i) + flightgroup:PassingWaypoint(i, final) -- If final waypoint reached, do route all over again. if i==final and final>1 then diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 1590e6052..0b46ef1f7 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -994,15 +994,15 @@ function CONTROLLABLE:TaskBombing( Vec2, GroupAttack, WeaponExpend, AttackQty, D return DCSTask end ---- (AIR) Attacking the map object (building, structure, e.t.c). +--- (AIR) Attacking the map object (building, structure, etc). -- @param #CONTROLLABLE self -- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. --- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. --- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #number Altitude (optional) The altitude from where to attack. --- @param #number WeaponType (optional) The WeaponType. +-- @param #boolean GroupAttack (Optional) If true, all units in the group will attack the Unit when found. +-- @param DCS#AI.Task.WeaponExpend WeaponExpend (Optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param #number AttackQty (Optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. +-- @param DCS#Azimuth Direction (Optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param #number Altitude (Optional) The altitude [meters] from where to attack. Default 30 m. +-- @param #number WeaponType (Optional) The WeaponType. Default Auto=1073741822. -- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskAttackMapObject( Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType ) self:F2( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType } ) @@ -1012,6 +1012,8 @@ function CONTROLLABLE:TaskAttackMapObject( Vec2, GroupAttack, WeaponExpend, Atta id = 'AttackMapObject', params = { point = Vec2, + x = Vec2.x, + y = Vec2.y, groupAttack = GroupAttack or false, expend = WeaponExpend or "Auto", attackQtyLimit = AttackQty and true or false, @@ -1020,7 +1022,7 @@ function CONTROLLABLE:TaskAttackMapObject( Vec2, GroupAttack, WeaponExpend, Atta direction = Direction, altitudeEnabled = Altitude and true or false, altitude = Altitude or 30, - weaponType = WeaponType, + weaponType = WeaponType or 1073741822, }, },