From 2840865b83d5e4c859583bd7b823d05ce77c59ba Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 27 Oct 2022 23:19:35 +0200 Subject: [PATCH] Up --- Moose Development/Moose/Ops/Auftrag.lua | 156 +++++++++++++++--- Moose Development/Moose/Ops/FlightGroup.lua | 37 +++++ Moose Development/Moose/Ops/OpsGroup.lua | 105 ++++++++---- Moose Development/Moose/Ops/Target.lua | 108 +++++++++++- .../Moose/Wrapper/Controllable.lua | 6 +- 5 files changed, 352 insertions(+), 60 deletions(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 895aaa387..0b8630958 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -59,6 +59,7 @@ -- @field #number Nelements Number of elements (units) assigned to mission. -- @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 #boolean updateDCSTask If `true`, DCS task is updated at every status update of the assigned groups. -- @field #table conditionStart Condition(s) that have to be true, before the mission will be started. -- @field #table conditionSuccess If all conditions are true, the mission is cancelled. -- @field #table conditionFailure If all conditions are true, the mission is cancelled. @@ -68,7 +69,9 @@ -- @field #number orbitAltitude Orbit altitude in meters. -- @field #number orbitHeading Orbit heading in degrees. -- @field #number orbitLeg Length of orbit leg in meters. --- @field Core.Point#COORDINATE orbitRaceTrack Race-track orbit coordinate. +-- @field DCS#Vec2 orbitOffsetVec2 2D offset vector. +-- @field DCS#Vec2 orbitVec2 2D orbit vector. +-- @field #number orbitDeltaR Distance threshold in meters for moving orbit targets. -- -- @field Ops.Target#TARGET engageTarget Target data to engage. -- @@ -1020,7 +1023,7 @@ end --- **[AIR]** Create an ORBIT mission, which can be either a circular orbit or a race-track pattern. -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Coordinate Where to orbit. --- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. +-- @param #number Altitude Orbit altitude in feet above sea level. Default is y component of `Coordinate`. -- @param #number Speed Orbit speed in knots. Default 350 KIAS. -- @param #number Heading Heading of race-track pattern in degrees. If not specified, a circular orbit is performed. -- @param #number Leg Length of race-track in NM. If not specified, a circular orbit is performed. @@ -1028,26 +1031,35 @@ end function AUFTRAG:NewORBIT(Coordinate, Altitude, Speed, Heading, Leg) local mission=AUFTRAG:New(AUFTRAG.Type.ORBIT) - - -- Altitude. + + -- Target. + mission:_TargetFromObject(Coordinate) + + -- Set Altitude. if Altitude then mission.orbitAltitude=UTILS.FeetToMeters(Altitude) else mission.orbitAltitude=Coordinate.y end - Coordinate.y=mission.orbitAltitude - - mission:_TargetFromObject(Coordinate) - - mission.orbitSpeed = UTILS.KnotsToMps(Speed or 350) -- the DCS Task itself will shortly be build with this so MPS + + -- Orbit speed in m/s. + mission.orbitSpeed = UTILS.KnotsToMps(Speed or 350) + + -- Mission speed in km/h. mission.missionSpeed = UTILS.KnotsToKmph(Speed or 350) - - if Heading and Leg then - mission.orbitHeading=Heading + + if Leg then mission.orbitLeg=UTILS.NMToMeters(Leg) - mission.orbitRaceTrack=Coordinate:Translate(mission.orbitLeg, mission.orbitHeading, true) - end + -- Relative heading + if Heading and Heading<0 then + mission.orbitHeadingRel=true + Heading=-Heading + end + + -- Heading if given. + mission.orbitHeading=Heading + end -- Mission options: mission.missionAltitude=mission.orbitAltitude*0.9 @@ -1093,6 +1105,32 @@ function AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) return mission end +--- **[AIR]** Create an ORBIT mission, where the aircraft will fly a circular or race-track pattern over a given group or unit. +-- @param #AUFTRAG self +-- @param Wrapper.Group#GROUP Group Group where to orbit around. Can also be a UNIT object. +-- @param #number Altitude Orbit altitude in feet. Default is 7,000 ft. +-- @param #number Speed Orbit speed in knots. Default 350 KIAS. +-- @param #number Leg Length of race-track in NM. Default nil. +-- @param #number Heading Heading of race-track pattern in degrees. Default is heading of the group. +-- @param DCS#Vec2 OffsetVec2 Offset 2D-vector in meters with respect to the group. Default `{x=0, y=0}`, *i.e.* directly overhead. +-- @param #number Distance Threshold distance in meters before orbit pattern is updated. Default 1000 m. +-- @return #AUFTRAG self +function AUFTRAG:NewORBIT_GROUP(Group, Altitude, Speed, Leg, Heading, OffsetVec2, Distance) + + Altitude = Altitude or 7000 + + + -- Create orbit mission. + local mission=AUFTRAG:NewORBIT(Group, Altitude, Speed, Heading, Leg) + + mission.updateDCSTask=true + mission.orbitOffsetVec2=OffsetVec2 or {x=0, y=0} + mission.orbitDeltaR=1000 + + return mission +end + + --- **[AIR]** Create a Ground Controlled CAP (GCICAP) mission. Flights with this task are considered for A2A INTERCEPT missions by the CHIEF class. They will perform a compat air patrol but not engage by -- themselfs. They wait for the CHIEF to tell them whom to engage. -- @param #AUFTRAG self @@ -4970,6 +5008,17 @@ function AUFTRAG:GetTargetCoordinate() return nil end +--- Get heading of target. +-- @param #AUFTRAG self +-- @return #number Heading of target in degrees. +function AUFTRAG:GetTargetHeading() + if self.engageTarget then + local heading=self.engageTarget:GetHeading() + return heading + end + return nil +end + --- Get name of the target. -- @param #AUFTRAG self -- @return #string Name of the target or "N/A". @@ -5235,6 +5284,17 @@ function AUFTRAG:_SetLogID() return self end + +--- Update DCS task. +-- @param #AUFTRAG self +-- @return #AUFTRAG self +function AUFTRAG:_UpdateTask() + + + + return self +end + --- Update mission F10 map marker. -- @param #AUFTRAG self -- @return #AUFTRAG self @@ -5892,11 +5952,67 @@ function AUFTRAG:GetDCSMissionTask() -- ORBIT Mission -- ------------------- - local Coordinate=self:GetTargetCoordinate() - - local DCStask=CONTROLLABLE.TaskOrbit(nil, Coordinate, self.orbitAltitude, self.orbitSpeed, self.orbitRaceTrack) - - table.insert(DCStasks, DCStask) + -- Get/update orbit vector. + self.orbitVec2=self:GetTargetVec2() + + if self.orbitVec2 then + + -- Check for race-track pattern. + local orbitRaceTrack=nil --DCS#Vec2 + if self.orbitLeg then + + -- Default heading is due North. + local heading=0 + + -- Check if specific heading was specified. + if self.orbitHeading then + + -- Is heading realtive to target? + if self.orbitHeadingRel then + -- Get heading of target. + local hdg=self:GetTargetHeading() + -- Relative heading wrt target. + heading=hdg+self.orbitHeading + else + -- Take given heading. + heading=self.orbitHeading + end + + else + -- Not specific heading specified ==> Take heading of target. + heading=self:GetTargetHeading() or 0 + end + + -- Race-track vector. + orbitRaceTrack=UTILS.Vec2Translate(self.orbitVec2, self.orbitLeg, heading) + + -- Debug show arrow. + COORDINATE:NewFromVec2(self.orbitVec2):ArrowToAll(COORDINATE:NewFromVec2(orbitRaceTrack)) + end + + local OffsetVec2=self.orbitOffsetVec2 and UTILS.DeepCopy(self.orbitOffsetVec2) or nil + if OffsetVec2 then + + env.info("FF 1000") + + OffsetVec2.x=self.orbitOffsetVec2.r and self.orbitOffsetVec2.r*math.cos(math.rad(self.orbitOffsetVec2.phi) or 0) or self.orbitOffsetVec2.x + OffsetVec2.y=self.orbitOffsetVec2.r and self.orbitOffsetVec2.r*math.sin(math.rad(self.orbitOffsetVec2.phi) or 0) or self.orbitOffsetVec2.y + + end + + -- Actual orbit position with possible offset. + local orbitVec2=OffsetVec2 and UTILS.Vec2Add(self.orbitVec2, OffsetVec2) or self.orbitVec2 + + -- Debug + local coord=COORDINATE:NewFromVec2(self.orbitVec2):MarkToAll("Orbit Center") + + -- Create orbit task. + local DCStask=CONTROLLABLE.TaskOrbit(nil, orbitVec2, self.orbitAltitude, self.orbitSpeed, orbitRaceTrack) + + -- Add DCS task. + table.insert(DCStasks, DCStask) + + end end @@ -6083,6 +6199,8 @@ function AUFTRAG.CheckMissionCapabilityAll(MissionTypes, Capabilities) return res end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 29271d3fd..67ac83786 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -899,6 +899,43 @@ function FLIGHTGROUP:Status() end end + -- Get current mission (if any). + local mission=self:GetMissionCurrent() + + -- If mission, check if DCS task needs to be updated. + if mission and mission.updateDCSTask then + + -- Orbit missions might need updates. + if mission:GetType()==AUFTRAG.Type.ORBIT and mission.orbitVec2 then + + -- Get 2D vector of orbit target. + local vec2=mission:GetTargetVec2() + + -- Distance to previous position. + local dist=UTILS.VecDist2D(vec2, mission.orbitVec2) + + -- Debug info. + self:I(self.lid..string.format("FF Checking orbit mission dist=%d meters", dist)) + + -- Check if distance is larger than threshold. + if dist>mission.orbitDeltaR then + + -- Update DCS task. This also sets the new mission.orbitVec2. + local DCSTask=mission:GetDCSMissionTask() --DCS#Task + + -- Get task. + local Task=self:GetTaskByID(mission.auftragsnummer) + + -- Reset current orbit task. + self.controller:resetTask() + + -- Push task after one second. We need to give resetTask some time or it will not work! + self:_SandwitchDCSTask(DCSTask, Task, false, 1) + + end + end + end + -- TODO: _CheckParking() function diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 92fc2614e..f9ebbc7e0 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -4070,6 +4070,23 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) -- Get mission of this task (if any). local Mission=self:GetMissionByTaskID(self.taskcurrent) + + self:_UpdateTask(Task, Mission) + + -- Set AUFTRAG status. + if Mission then + self:MissionExecute(Mission) + end + +end + +--- Push task +-- @param #OPSGROUP self +-- @param Ops.OpsGroup#OPSGROUP.Task Task The task. +function OPSGROUP:_UpdateTask(Task, Mission) + + local Mission=Mission or self:GetMissionByTaskID(self.taskcurrent) + if Task.dcstask.id==AUFTRAG.SpecialTask.FORMATION then -- Set of group(s) to follow Mother. @@ -4434,39 +4451,8 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) --- DCSTask=Task.dcstask end - - local DCStasks={} - if DCSTask.id=='ComboTask' then - -- Loop over all combo tasks. - for TaskID, Task in ipairs(DCSTask.params.tasks) do - table.insert(DCStasks, Task) - end - else - table.insert(DCStasks, DCSTask) - end - - -- Combo task. - local TaskCombo=self.group:TaskCombo(DCStasks) - - -- Stop condition! - local TaskCondition=self.group:TaskCondition(nil, Task.stopflag:GetName(), 1, nil, Task.duration) - - -- Controlled task. - local TaskControlled=self.group:TaskControlled(TaskCombo, TaskCondition) - - -- Task done. - local TaskDone=self.group:TaskFunction("OPSGROUP._TaskDone", self, Task) - - -- Final task. - local TaskFinal=self.group:TaskCombo({TaskControlled, TaskDone}) - - -- Set task for group. - -- NOTE: I am pushing the task instead of setting it as it seems to keep the mission task alive. - -- There were issues that flights did not proceed to a later waypoint because the task did not finish until the fired missiles - -- impacted (took rather long). Then the flight flew to the nearest airbase and one lost completely the control over the group. - self:PushTask(TaskFinal) - --self:SetTask(TaskFinal) - + + self:_SandwitchDCSTask(DCSTask, Task) elseif Task.type==OPSGROUP.TaskType.WAYPOINT then -- Waypoint tasks are executed elsewhere! @@ -4475,15 +4461,62 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) end end + +end +--- Sandwitch DCS task in stop condition and push the task to the group. +-- @param #OPSGROUP self +-- @param DCS#Task DCSTask The DCS task. +-- @param Ops.OpsGroup#OPSGROUP.Task Task +-- @param #boolean SetTask Set task instead of pushing it. +-- @param #number Delay Delay in seconds. Default nil. +function OPSGROUP:_SandwitchDCSTask(DCSTask, Task, SetTask, Delay) - -- Set AUFTRAG status. - if Mission then - self:MissionExecute(Mission) + if Delay and Delay>0 then + -- Delayed call. + self:ScheduleOnce(Delay, OPSGROUP._SandwitchDCSTask, self, DCSTask, Task, SetTask) + else + + local DCStasks={} + if DCSTask.id=='ComboTask' then + -- Loop over all combo tasks. + for TaskID, Task in ipairs(DCSTask.params.tasks) do + table.insert(DCStasks, Task) + end + else + table.insert(DCStasks, DCSTask) + end + + -- Combo task. + local TaskCombo=self.group:TaskCombo(DCStasks) + + -- Stop condition! + local TaskCondition=self.group:TaskCondition(nil, Task.stopflag:GetName(), 1, nil, Task.duration) + + -- Controlled task. + local TaskControlled=self.group:TaskControlled(TaskCombo, TaskCondition) + + -- Task done. + local TaskDone=self.group:TaskFunction("OPSGROUP._TaskDone", self, Task) + + -- Final task. + local TaskFinal=self.group:TaskCombo({TaskControlled, TaskDone}) + + -- Set task for group. + -- NOTE: I am pushing the task instead of setting it as it seems to keep the mission task alive. + -- There were issues that flights did not proceed to a later waypoint because the task did not finish until the fired missiles + -- impacted (took rather long). Then the flight flew to the nearest airbase and one lost completely the control over the group. + if SetTask then + self:SetTask(TaskFinal) + else + self:PushTask(TaskFinal) + end + end end + --- On after "TaskCancel" event. Cancels the current task or simply sets the status to DONE if the task is not the current one. -- @param #OPSGROUP self -- @param #string From From state. diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 09fb781fa..1bca8d312 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -1019,7 +1019,7 @@ function TARGET:GetTargetLife(Target) end ---- Get current life points. +--- Get current total life points. This is the sum of all target objects. -- @param #TARGET self -- @return #number Life points of target. function TARGET:GetLife() @@ -1221,6 +1221,89 @@ function TARGET:GetTargetVec3(Target, Average) self:E(self.lid.."ERROR: Unknown TARGET type! Cannot get Vec3") end +--- Get heading of the target. +-- @param #TARGET self +-- @param #TARGET.Object Target Target object. +-- @return #number Heading in degrees. +function TARGET:GetTargetHeading(Target) + + if Target.Type==TARGET.ObjectType.GROUP then + + local object=Target.Object --Wrapper.Group#GROUP + + if object and object:IsAlive() then + local heading=object:GetHeading() + + if heading then + return heading + else + return nil + end + else + + return nil + + end + + elseif Target.Type==TARGET.ObjectType.UNIT then + + local object=Target.Object --Wrapper.Unit#UNIT + + if object and object:IsAlive() then + local heading=object:GetHeading() + return heading + else + return nil + end + + elseif Target.Type==TARGET.ObjectType.STATIC then + + local object=Target.Object --Wrapper.Static#STATIC + + if object and object:IsAlive() then + local heading=object:GetHeading() + return heading + else + return nil + end + + elseif Target.Type==TARGET.ObjectType.SCENERY then + + local object=Target.Object --Wrapper.Scenery#SCENERY + + if object then + local heading=object:GetHeading() + return heading + else + return nil + end + + elseif Target.Type==TARGET.ObjectType.AIRBASE then + + local object=Target.Object --Wrapper.Airbase#AIRBASE + + -- Airbase has no real heading. Return 0. Maybe take the runway heading? + return 0 + + elseif Target.Type==TARGET.ObjectType.COORDINATE then + + local object=Target.Object --Core.Point#COORDINATE + + -- A coordinate has no heading. Return 0. + return 0 + + elseif Target.Type==TARGET.ObjectType.ZONE then + + local object=Target.Object --Core.Zone#ZONE + + -- A zone has no heading. Return 0. + return 0 + + end + + self:E(self.lid.."ERROR: Unknown TARGET type! Cannot get heading") +end + --- Get target coordinate. -- @param #TARGET self @@ -1378,7 +1461,7 @@ function TARGET:GetAverageCoordinate() for _,_target in pairs(self.targets) do local Target=_target --#TARGET.Object - local coordinate=self:GetTargetCoordinate(Target,true) + local coordinate=self:GetTargetCoordinate(Target, true) if coordinate then return coordinate @@ -1390,6 +1473,26 @@ function TARGET:GetAverageCoordinate() return nil end +--- Get heading of target. +-- @param #TARGET self +-- @return #number Heading of the target in degrees. +function TARGET:GetHeading() + + for _,_target in pairs(self.targets) do + local Target=_target --#TARGET.Object + + local heading=self:GetTargetHeading(Target) + + if heading then + return heading + end + + end + + self:E(self.lid..string.format("ERROR: Cannot get heading of target %s", tostring(self.name))) + return nil +end + --- Get category. -- @param #TARGET self -- @return #string Target category. See `TARGET.Category.X`, where `X=AIRCRAFT, GROUND`. @@ -1397,6 +1500,7 @@ function TARGET:GetCategory() return self.category end + --- Get target category. -- @param #TARGET self -- @param #TARGET.Object Target Target object. diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index e5a165ea1..42a145312 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1259,7 +1259,7 @@ end --- (AIR) Orbit at a position with at a given altitude and speed. Optionally, a race track pattern can be specified. -- @param #CONTROLLABLE self --- @param Core.Point#COORDINATE Coord Coordinate at which the CONTROLLABLE orbits. +-- @param Core.Point#COORDINATE Coord Coordinate at which the CONTROLLABLE orbits. Can also be given as a `DCS#Vec3` or `DCS#Vec2` object. -- @param #number Altitude Altitude in meters of the orbit pattern. Default y component of Coord. -- @param #number Speed Speed [m/s] flying the orbit pattern. Default 128 m/s = 250 knots. -- @param Core.Point#COORDINATE CoordRaceTrack (Optional) If this coordinate is specified, the CONTROLLABLE will fly a race-track pattern using this and the initial coordinate. @@ -1268,11 +1268,11 @@ function CONTROLLABLE:TaskOrbit( Coord, Altitude, Speed, CoordRaceTrack ) local Pattern = AI.Task.OrbitPattern.CIRCLE - local P1 = Coord:GetVec2() + local P1 = {x=Coord.x, y=Coord.z or Coord.y} local P2 = nil if CoordRaceTrack then Pattern = AI.Task.OrbitPattern.RACE_TRACK - P2 = CoordRaceTrack:GetVec2() + P2 = {x=CoordRaceTrack.x, y=CoordRaceTrack.z or CoordRaceTrack.y} end local Task = {