diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 1facff9ee..882346f24 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -37,6 +37,8 @@ -- @field #number markerID F10 map marker ID. -- @field #table DCStask DCS task structure. -- @field #number Ntargets Number of mission targets. +-- @field #number dTevaluate Time interval in seconds before the mission result is evaluated after mission is over. +-- @field #number Tover Mission abs. time stamp, when mission was over. -- -- @field Core.Point#COORDINATE orbitCoord Coordinate where to orbit. -- @field #number orbitSpeed Orbit speed in m/s. @@ -308,6 +310,8 @@ function AUFTRAG:New(Type) self.nassets=1 + self.dTevaluate=0 + -- FMS start state is PLANNED. self:SetStartState(self.status) @@ -590,7 +594,7 @@ end -- @param Core.Zone#ZONE_RADIUS ZoneCAS Circular CAS zone. Detected targets in this zone will be engaged. -- @param #table TargetTypes Table of target types. Default {"Helicopters", "Ground Units", "Light armed ships"}. -- @return #AUFTRAG self -function AUFTRAG:NewFACA(OrbitCoordinate, OrbitSpeed, Heading, Leg, ZoneCAS, TargetTypes) +function AUFTRAG:NewFACA(TargetGroup) -- Ensure given TargetTypes parameter is a table. if TargetTypes then @@ -715,6 +719,9 @@ function AUFTRAG:NewBOMBING(Target, Altitude) mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.PassiveDefense + -- Evaluate result after 5 min. We might need time until the bombs have dropped and targets have been detroyed. + mission.dTevaluate=5*60 + -- Get DCS task. mission.DCStask=mission:GetDCSMissionTask() @@ -747,6 +754,9 @@ function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude) mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.PassiveDefense + -- Evaluate result after 5 min. + mission.dTevaluate=5*60 + -- Get DCS task. mission.DCStask=mission:GetDCSMissionTask() @@ -1160,6 +1170,7 @@ function AUFTRAG:onafterStatus(From, Event, To) -- Current FSM state. local fsmstate=self:GetState() + local Tnow=timer.getAbsTime() -- Mission start stop time. local Cstart=UTILS.SecondsToClock(self.Tstart, true) @@ -1187,8 +1198,10 @@ function AUFTRAG:onafterStatus(From, Event, To) self:I(self.lid..text) end - -- Check if mission is OVER (done or cancelled). - if self:IsOver() then + local ready2evaluate=self.Tover and Tnow-self.Tover>self.dTevaluate or false + + -- Check if mission is OVER (done or cancelled) and enough time passed to evaluate the result. + if self:IsOver() and ready2evaluate then -- Evaluate success or failure of the mission. self:Evaluate() else @@ -1484,6 +1497,9 @@ end function AUFTRAG:onafterDone(From, Event, To) self.status=AUFTRAG.Status.DONE self:I(self.lid..string.format("New mission status=%s", self.status)) + + -- Set time stamp. + self.Tover=timer.getAbsTime() end @@ -1495,7 +1511,7 @@ end -- @param Ops.FlightGroup#FLIGHTGROUP FlightGroup The flightgroup that is dead now. function AUFTRAG:onafterFlightDead(From, Event, To, FlightGroup) - self:SetFlightStatus(FlightGroup, AUFTRAG.FlightStatus.DONE) + --self:SetFlightStatus(FlightGroup, AUFTRAG.FlightStatus.DONE) local asset=self:GetAssetByName(FlightGroup.groupname) if asset then @@ -1568,22 +1584,31 @@ function AUFTRAG:onafterCancel(From, Event, To) -- Debug info. self:I(self.lid..string.format("CANCELLING mission in status %s. Will wait for flights to report mission DONE before evaluation.", self.status)) + -- Time stamp. + self.Tover=timer.getAbsTime() + + -- No more repeats. + self.missionRepeatMax=self.missionRepeated + + -- Not necessary to delay the evaluaton?! + self.dTevaluate=0 + if self.wingcommander then - env.info(string.format("FF calling CancelMission() for wingcommander")) + self:I(self.lid..string.format("Wingcommander will cancel the mission. Will wait for mission DONE before evaluation!")) self.wingcommander:CancelMission(self) elseif self.airwing then - env.info(string.format("FF calling MissionCancel() for airwing %s", self.airwing.alias)) + self:I(self.lid..string.format("Airwing %s will cancel the mission. Will wait for mission DONE before evaluation!", self.airwing.alias)) -- Airwing will cancel all flight missions and remove queued request from warehouse queue. self.airwing:MissionCancel(self) else - env.info(string.format("FF no airwing. cancelling flihgts")) + self:I(self.lid..string.format("No airwing or wingcommander. Attached flights will cancel the mission on their own. Will wait for mission DONE before evaluation!")) for _,_flightdata in pairs(self.flightdata) do local flightdata=_flightdata --#AUFTRAG.FlightData @@ -1612,13 +1637,13 @@ function AUFTRAG:onafterFailed(From, Event, To) if self.missionRepeated>=self.missionRepeatMax then - self:I(self.lid..string.format("Mission failed! Number of max repeats reached %d>=%d ==> Stopping mission!", self.missionRepeated, self.missionRepeatMax)) + self:I(self.lid..string.format("Mission FAILED! Number of max repeats reached [%d>=%d] ==> Stopping mission!", self.missionRepeated, self.missionRepeatMax)) self:Stop() else -- Repeat mission. - self:I(self.lid..string.format("Mission failed! Repeating mission for the %d. time (max=%d) ==> Repeat mission!", self.missionRepeated+1, self.missionRepeatMax)) + self:I(self.lid..string.format("Mission failed! Repeating mission for the %d time (max %d times) ==> Repeat mission!", self.missionRepeated+1, self.missionRepeatMax)) self:Repeat() end diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index fde94493c..5f46413f2 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -86,6 +86,7 @@ -- -- @field Core.Point#COORDINATE position Current position of the group. -- @field #number traveldist Distance traveled in meters. This is a lower bound! +-- @field #number traveltime Time. -- @field #number fuelmass Inital fuel in kg. -- -- @extends Core.Fsm#FSM @@ -1632,9 +1633,26 @@ function FLIGHTGROUP:onafterFlightStatus(From, Event, To) end --- - -- Tasks + -- Distance travelled --- + + local position=self:GetCoordinate() + local dist=self.position:Get3DDistance(position) + local time=timer.getAbsTime() + + local vel=dist/(time-self.traveltime) + + self.traveldist=self.traveldist+dist + self.traveltime=time + self.position=position + + env.info(string.format("FF Distance travelled = %.1f km v=%.1f knots", self.traveldist/1000, UTILS.MpsToKnots(vel))) + + --- + -- Tasks + --- + -- Task queue. if #self.taskqueue>0 and self.verbose>1 then local text=string.format("Tasks #%d", #self.taskqueue) @@ -1690,10 +1708,6 @@ function FLIGHTGROUP:onafterFlightStatus(From, Event, To) --- -- Fuel State --- - - local position=self:GetCoordinate() - local dist=self.position:Get3DDistance(position) - self.traveldist=self.traveldist+dist -- Only if group is in air. @@ -2500,9 +2514,11 @@ function FLIGHTGROUP:onafterFlightDead(From, Event, To) self.flightcontrol=nil end + -- Cancel all mission. for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG - + + self:MissionCancel(mission) mission:FlightDead(self) end @@ -2728,47 +2744,52 @@ end -- @param #number delay Delay in seconds. function FLIGHTGROUP:_CheckFlightDone(delay) - if delay and delay>0 then - -- Delayed call. - self:ScheduleOnce(delay, FLIGHTGROUP._CheckFlightDone, self) - else - - local nTasks=self:CountRemainingTasks() - local nMissions=self:CountRemainingMissison() - - -- Final waypoint passed? - if self.passedfinalwp then - - -- Got current mission or task? - if self.currentmission==nil and self.taskcurrent==0 then - - -- Number of remaining tasks/missions? - if nTasks==0 and nMissions==0 then - - -- Send flight to destination. - if self.destbase then - self:I(self.lid.."Passed Final WP and No current and/or future missions/task ==> RTB!") - self:__RTB(-1, self.destbase) - elseif self.destzone then - self:I(self.lid.."Passed Final WP and No current and/or future missions/task ==> RTZ!") - self:__RTZ(-1, self.destzone) - else - self:I(self.lid.."Passed Final WP and NO Tasks/Missions left. No DestBase or DestZone ==> Wait!") - self:__Wait(-1) - end - - else - self:I(self.lid..string.format("Passed Final WP but Tasks=%d or Missions=%d left in the queue. Wait!", nTasks, nMissions)) - self:__Wait(-1) - end - else - self:I(self.lid..string.format("Passed Final WP but still have current Tasks (%s) or Missions (%s) left to do", tostring(self.taskcurrent), tostring(self.currentmission))) - end + if self:IsAlive() then + + if delay and delay>0 then + -- Delayed call. + self:ScheduleOnce(delay, FLIGHTGROUP._CheckFlightDone, self) else - self:I(self.lid..string.format("Flight (status=%s) did NOT pass the final waypoint yet ==> update route", self:GetState())) - self:__UpdateRoute(-1) - end + + local nTasks=self:CountRemainingTasks() + local nMissions=self:CountRemainingMissison() + + -- Final waypoint passed? + if self.passedfinalwp then + + -- Got current mission or task? + if self.currentmission==nil and self.taskcurrent==0 then + + -- Number of remaining tasks/missions? + if nTasks==0 and nMissions==0 then + + -- Send flight to destination. + if self.destbase then + self:I(self.lid.."Passed Final WP and No current and/or future missions/task ==> RTB!") + self:__RTB(-1, self.destbase) + elseif self.destzone then + self:I(self.lid.."Passed Final WP and No current and/or future missions/task ==> RTZ!") + self:__RTZ(-1, self.destzone) + else + self:I(self.lid.."Passed Final WP and NO Tasks/Missions left. No DestBase or DestZone ==> Wait!") + self:__Wait(-1) + end + + else + self:I(self.lid..string.format("Passed Final WP but Tasks=%d or Missions=%d left in the queue. Wait!", nTasks, nMissions)) + self:__Wait(-1) + end + else + self:I(self.lid..string.format("Passed Final WP but still have current Tasks (%s) or Missions (%s) left to do", tostring(self.taskcurrent), tostring(self.currentmission))) + end + else + self:I(self.lid..string.format("Flight (status=%s) did NOT pass the final waypoint yet ==> update route", self:GetState())) + self:__UpdateRoute(-1) + end + end + end + end --- On after "GotoWaypoint" event. Group will got to the given waypoint and execute its route from there. @@ -3537,18 +3558,21 @@ function FLIGHTGROUP:onafterTaskCancel(From, Event, To, Task) Task.stopflag:Set(1) else - - -- Set task status to DONE (we have no CANCELLED defined yet). - Task.status=FLIGHTGROUP.TaskStatus.DONE - + -- Debug info. self:I(self.lid..string.format("TaskCancel: Setting task %s ID=%d to DONE", Task.description, Task.id)) + -- Call task done function. + self:TaskDone(Task) + + + local mission=self:GetMissionByTaskID(Task.id) + -- Is this a waypoint task? if Task.type==FLIGHTGROUP.TaskType.WAYPOINT and Task.waypoint then -- Check that this is a mission waypoint and no other tasks are defined here. - if self:GetMissionByTaskID(Task.id) and #self:GetTasksWaypoint(Task.waypoint) then + if mission and #self:GetTasksWaypoint(Task.waypoint) then self:RemoveWaypoint(Task.waypoint) end end @@ -3595,7 +3619,9 @@ function FLIGHTGROUP:onafterTaskDone(From, Event, To, Task) self:I(self.lid..text) -- No current task. - self.taskcurrent=0 + if Task.id==self.taskcurrent then + self.taskcurrent=0 + end -- Task status done. Task.status=FLIGHTGROUP.TaskStatus.DONE @@ -4000,6 +4026,7 @@ function FLIGHTGROUP:_InitGroup() self.fuelmass=0 --self.template.pylons.fuel self.traveldist=0 + self.traveltime=timer.getAbsTime() self.position=self:GetCoordinate() -- Radio parameters from template. @@ -4645,29 +4672,33 @@ end -- @return #FLIGHTGROUP self function FLIGHTGROUP:RemoveWaypoint(wpindex) - -- Remove waypoint. - table.remove(self.waypoints, wpindex) + if self.waypoints then - -- Shift all waypoint tasks after the removed waypoint. - for _,_task in pairs(self.taskqueue) do - local task=_task --#FLIGHTGROUP.Task - if task.type==FLIGHTGROUP.TaskType.WAYPOINT and task.waypoint and task.waypoint>wpindex then - task.waypoint=task.waypoint-1 - end - end - - -- Shift all mission waypoints after the removerd waypoint. - for _,_mission in pairs(self.missionqueue) do - local mission=_mission --Ops.Auftrag#AUFTRAG - if mission.waypointindex and mission.waypointindex>wpindex then - mission.waypointindex=mission.waypointindex-1 - end - end - - --TODO update route? - -- no, if <= self.currentwaypoint or WP is landing. - - self:__UpdateRoute(-1) + -- Remove waypoint. + table.remove(self.waypoints, wpindex) + + -- Shift all waypoint tasks after the removed waypoint. + for _,_task in pairs(self.taskqueue) do + local task=_task --#FLIGHTGROUP.Task + if task.type==FLIGHTGROUP.TaskType.WAYPOINT and task.waypoint and task.waypoint>wpindex then + task.waypoint=task.waypoint-1 + end + end + + -- Shift all mission waypoints after the removerd waypoint. + for _,_mission in pairs(self.missionqueue) do + local mission=_mission --Ops.Auftrag#AUFTRAG + if mission.waypointindex and mission.waypointindex>wpindex then + mission.waypointindex=mission.waypointindex-1 + end + end + + --TODO update route? + -- no, if <= self.currentwaypoint or WP is landing. + + self:__UpdateRoute(-1) + + end end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index b5b424f51..a10a6a452 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1428,8 +1428,12 @@ end -- @param #number WeaponType Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. -- @param DCS#AI.Task.Designation Designation (optional) Designation type. -- @param #boolean Datalink (optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default. +-- @param #number Frequency Frequency used to communicate with the FAC. +-- @param #number Modulation Modulation of radio for communication. +-- @param #number CallsignName Callsign enumerator name of the FAC. +-- @param #number CallsignNumber Callsign number, e.g. Axeman-**1**. -- @return DCS#Task The DCS task structure. -function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink ) +function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink, Frequency, Modulation, CallsignName, CallsignNumber ) local DCSTask = { id = 'FAC_AttackGroup', @@ -1438,6 +1442,10 @@ function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, weaponType = WeaponType, designation = Designation, datalink = Datalink, + frequency = Frequency, + modulation = Modulation, + callname = CallsignName, + number = CallsignNumber, } } @@ -1776,29 +1784,6 @@ function CONTROLLABLE:TaskDisembarkFromTransport(Coordinate, Radius) end ]] ---- (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable. --- @param #CONTROLLABLE self --- @param DCS#Vec2 Point The point where to wait. --- @param #number Duration The duration in seconds to wait. --- @param #CONTROLLABLE EmbarkingControllable The controllable to be embarked. --- @return DCS#Task The DCS task structure -function CONTROLLABLE:TaskEmbarking( Point, Duration, EmbarkingControllable ) - - local DCSTask = { - id = 'Embarking', - params = { - x = Point.x, - y = Point.y, - duration = Duration, - controllablesForEmbarking = { EmbarkingControllable.ControllableID }, - durationFlag = true, - distributionFlag = false, - distribution = {}, - } - } - - return DCSTask -end --- (GROUND) Embark to a Transport landed at a location. -- Move to a defined Vec2 Point, and embark to a controllable when arrived within a defined Radius.