From 27632ecdd988e37dd1dc2cc4fe743672b210aebd Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 1 Sep 2021 00:07:54 +0200 Subject: [PATCH] OPSTRANSPORT v0.4.1 - Fixed a couple of bugs - Lots of other fixes and improvements --- Moose Development/Moose/Core/Base.lua | 6 +- Moose Development/Moose/Core/Set.lua | 14 ++ Moose Development/Moose/Ops/ArmyGroup.lua | 14 +- Moose Development/Moose/Ops/FlightGroup.lua | 30 ++-- Moose Development/Moose/Ops/OpsGroup.lua | 51 ++++--- Moose Development/Moose/Ops/OpsTransport.lua | 144 ++++++++++++++++++- 6 files changed, 217 insertions(+), 42 deletions(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 6c4d6a8eb..2c492a949 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -875,12 +875,12 @@ do -- Scheduling -- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }. -- @return #string The Schedule ID of the planned schedule. function BASE:ScheduleOnce( Start, SchedulerFunction, ... ) - self:F2( { Start } ) - self:T3( { ... } ) + -- Object name. local ObjectName = "-" ObjectName = self.ClassName .. self.ClassID + -- Debug info. self:F3( { "ScheduleOnce: ", ObjectName, Start } ) if not self.Scheduler then @@ -930,7 +930,7 @@ do -- Scheduling self.Scheduler = SCHEDULER:New( self ) end - -- NOTE: MasterObject (first parameter) should(!) be nil as it will be the first argument passed to the SchedulerFunction! + -- NOTE: MasterObject (first parameter) should(!) be nil as it will be the first argument passed to the SchedulerFunction!s local ScheduleID = self.Scheduler:Schedule( self, SchedulerFunction, diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 17feaa492..017df9cb1 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -278,6 +278,20 @@ do -- SET_BASE end + --- Add a SET to this set. + -- @param #SET_BASE self + -- @param Core.Set#SET_BASE SetToAdd Set to add. + -- @return #SET_BASE self + function SET_BASE:AddSet(SetToAdd) + + for _,ObjectB in pairs(SetToAdd.Set) do + self:AddObject(ObjectB) + end + + return self + end + + --- Get the *union* of two sets. -- @param #SET_BASE self -- @param Core.Set#SET_BASE SetB Set *B*. diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index fc1761600..3adde055d 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -715,7 +715,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation) -- Passed final WP ==> Full Stop --- - self:E(self.lid..string.format("WARNING: Passed final WP ==> Full Stop!")) + self:E(self.lid..string.format("WARNING: Passed final WP when UpdateRoute() ==> Full Stop!")) self:FullStop() end @@ -991,6 +991,7 @@ end -- @param #string To To state. -- @param Wrapper.Group#GROUP Group the group to be engaged. function ARMYGROUP:onafterEngageTarget(From, Event, To, Target) + self:T(self.lid.."Engaging Target") if Target:IsInstanceOf("TARGET") then self.engage.Target=Target @@ -1062,6 +1063,8 @@ end -- @param #string Event Event. -- @param #string To To state. function ARMYGROUP:onafterDisengage(From, Event, To) + self:T(self.lid.."Disengage Target") + -- TODO: Reset ROE and alarm state. self:_CheckGroupDone(1) end @@ -1072,9 +1075,11 @@ end -- @param #string Event Event. -- @param #string To To state. function ARMYGROUP:onafterRearmed(From, Event, To) + self:I(self.lid.."Group rearmed") + -- Check group done. self:_CheckGroupDone(1) - + end --- On after "DetourReached" event. @@ -1083,7 +1088,7 @@ end -- @param #string Event Event. -- @param #string To To state. function ARMYGROUP:onafterDetourReached(From, Event, To) - self:I(self.lid.."Group reached detour coordinate.") + self:T(self.lid.."Group reached detour coordinate") end @@ -1094,7 +1099,8 @@ end -- @param #string To To state. function ARMYGROUP:onafterFullStop(From, Event, To) - self:I(self.lid..string.format("Full stop!")) + -- Debug info. + self:T(self.lid..string.format("Full stop!")) -- Get current position. local pos=self:GetCoordinate() diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index ea0a73eea..a8a4c19fe 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -1179,6 +1179,7 @@ end -- @param #FLIGHTGROUP self -- @param Core.Event#EVENTDATA EventData Event data. function FLIGHTGROUP:OnEventTakeOff(EventData) + self:T3(self.lid.."EVENT: TakeOff") -- Check that this is the right group. if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then @@ -1190,7 +1191,7 @@ function FLIGHTGROUP:OnEventTakeOff(EventData) local element=self:GetElementByName(unitname) if element then - self:T3(self.lid..string.format("EVENT: Element %s took off ==> airborne", element.name)) + self:T2(self.lid..string.format("EVENT: Element %s took off ==> airborne", element.name)) self:ElementTakeoff(element, EventData.Place) end @@ -1812,6 +1813,10 @@ function FLIGHTGROUP:onafterLanded(From, Event, To, airbase) -- Add flight to taxiinb queue. self.flightcontrol:SetFlightStatus(self, FLIGHTCONTROL.FlightStatus.TAXIINB) end + + if airbase and self.isHelo then + self:Arrived() + end end @@ -2020,19 +2025,21 @@ function FLIGHTGROUP:onbeforeUpdateRoute(From, Event, To, n) if task then if task.dcstask.id=="PatrolZone" then -- For patrol zone, we need to allow the update as we insert new waypoints. + self:T2(self.lid.."Allowing update route for Task: PatrolZone") elseif task.dcstask.id=="ReconMission" then -- For recon missions, we need to allow the update as we insert new waypoints. + self:T2(self.lid.."Allowing update route for Task: ReconMission") elseif task.description and task.description=="Task_Land_At" then -- We allow this - env.info("FF allowing update route for Task_Land_At") + self:T2(self.lid.."Allowing update route for Task: Task_Land_At") else local taskname=task and task.description or "No description" self:E(self.lid..string.format("WARNING: Update route denied because taskcurrent=%d>0! Task description = %s", self.taskcurrent, tostring(taskname))) allowed=false end else - -- Now this can happen, if we directly use TaskExecute as the task is not in the task queue and cannot be removed. - self:T(self.lid..string.format("WARNING: before update route taskcurrent=%d>0 but no task?!", self.taskcurrent)) + -- Now this can happen, if we directly use TaskExecute as the task is not in the task queue and cannot be removed. Therefore, also directly executed tasks should be added to the queue! + self:T(self.lid..string.format("WARNING: before update route taskcurrent=%d (>0!) but no task?!", self.taskcurrent)) -- Anyhow, a task is running so we do not allow to update the route! allowed=false end @@ -2102,15 +2109,6 @@ function FLIGHTGROUP:onafterUpdateRoute(From, Event, To, n) local hb=self.homebase and self.homebase:GetName() or "unknown" local db=self.destbase and self.destbase:GetName() or "unknown" self:T(self.lid..string.format("Updating route for WP #%d-%d [%s], homebase=%s destination=%s", n, #wp, self:GetState(), hb, db)) - - -- Print waypoints. - --[[ - for i,w in pairs(wp) do - env.info("FF waypoint index="..i) - self:I(w) - end - ]] - if #wp>1 then @@ -2489,6 +2487,10 @@ function FLIGHTGROUP:_LandAtAirbase(airbase, SpeedTo, SpeedHold, SpeedLand) -- Add flight to inbound queue. self.flightcontrol:SetFlightStatus(self, FLIGHTCONTROL.FlightStatus.INBOUND) end + + -- Some intermediate coordinate to climb to the default cruise alitude. + local c1=c0:GetIntermediateCoordinate(p0, 0.25):SetAltitude(self.altitudeCruise, true) + local c2=c0:GetIntermediateCoordinate(p0, 0.75):SetAltitude(self.altitudeCruise, true) -- Altitude above ground for a glide slope of 3 degrees. local x1=self.isHelo and UTILS.NMToMeters(5.0) or UTILS.NMToMeters(10) @@ -2521,6 +2523,8 @@ function FLIGHTGROUP:_LandAtAirbase(airbase, SpeedTo, SpeedHold, SpeedLand) -- Waypoints from current position to holding point. local wp={} wp[#wp+1]=c0:WaypointAir(nil, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, UTILS.KnotsToKmph(SpeedTo), true , nil, {}, "Current Pos") + wp[#wp+1]=c1:WaypointAir(nil, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, UTILS.KnotsToKmph(SpeedTo), true , nil, {}, "Climb") + wp[#wp+1]=c2:WaypointAir(nil, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, UTILS.KnotsToKmph(SpeedTo), true , nil, {}, "Descent") wp[#wp+1]=p0:WaypointAir(nil, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, UTILS.KnotsToKmph(SpeedTo), true , nil, {TaskArrived, TaskHold, TaskKlar}, "Holding Point") -- Approach point: 10 NN in direction of runway. diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index a1db9890f..ad94fc415 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -712,8 +712,8 @@ function OPSGROUP:New(group) -- @param #OPSGROUP self -- @param Ops.Auftrag#AUFTRAG Mission The mission. - --- Triggers the FSM event "MissionCancel" after a delay - -- @function [parent=#OPSGROUP] MissionCancel + --- Triggers the FSM event "MissionCancel" after a delay. + -- @function [parent=#OPSGROUP] __MissionCancel -- @param #OPSGROUP self -- @param #number delay Delay in seconds. -- @param Ops.Auftrag#AUFTRAG Mission The mission. @@ -1198,6 +1198,11 @@ function OPSGROUP:GetVec3(UnitName) end end + + -- Return last known position. + if self.position then + return self.position + end return nil end @@ -1806,6 +1811,8 @@ function OPSGROUP:IsInZone(Zone) local is=false if vec2 then is=Zone:IsVec2InZone(vec2) + else + env.info(self.lid.."FF cannot get vec2") end return is end @@ -1901,11 +1908,11 @@ function OPSGROUP:IsLateActivated() return self.isLateActivated end ---- Check if group is in state in utero. +--- Check if group is in state in utero. Note that dead groups are also in utero but will return `false` here. -- @param #OPSGROUP self -- @return #boolean If true, group is not spawned yet. function OPSGROUP:IsInUtero() - local is=self:Is("InUtero") + local is=self:Is("InUtero") and not self:IsDead() return is end @@ -2047,9 +2054,10 @@ end --- Check if the group is assigned as cargo. -- @param #OPSGROUP self +-- @param #boolean CheckTransport If `true` or `nil`, also check if cargo is associated with a transport assignment. If not, we consider it not cargo. -- @return #boolean If true, group is cargo. -function OPSGROUP:IsCargo() - return not self:IsNotCargo() +function OPSGROUP:IsCargo(CheckTransport) + return not self:IsNotCargo(CheckTransport) end --- Check if the group is **not** cargo. @@ -3310,8 +3318,8 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) -- 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:PushTask(TaskFinal) + self:SetTask(TaskFinal) elseif Task.type==OPSGROUP.TaskType.WAYPOINT then @@ -5567,19 +5575,19 @@ function OPSGROUP:_CheckCargoTransport() if self:IsNotCarrier() then - -- Debug info. - self:T(self.lid.."Not carrier ==> pickup?") - -- Get transport zone combo (TZC). self.cargoTZC=self.cargoTransport:_GetTransportZoneCombo(self) if self.cargoTZC then + + -- Found TZC + self:T(self.lid..string.format("Not carrier ==> pickup at %s [TZC UID=%d]", self.cargoTZC.PickupZone and self.cargoTZC.PickupZone:GetName() or "unknown", self.cargoTZC.uid)) -- Initiate the cargo transport process. self:__Pickup(-1) else - self:T(self.lid.."Not carrier ==> pickup") + self:T2(self.lid.."Not carrier ==> No TZC found") end elseif self:IsPickingup() then @@ -6422,7 +6430,7 @@ function OPSGROUP:onafterPickup(From, Event, To) end -- If this is a helo and no ZONE_AIRBASE was given, we make the helo land in the pickup zone. - local waypoint=FLIGHTGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid, self.altitudeCruise, false) ; waypoint.detour=1 + local waypoint=FLIGHTGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid, UTILS.MetersToFeet(self.altitudeCruise), false) ; waypoint.detour=1 else self:E(self.lid.."ERROR: Transportcarrier aircraft cannot land in Pickup zone! Specify a ZONE_AIRBASE as pickup zone") @@ -6513,10 +6521,13 @@ function OPSGROUP:onafterLoading(From, Event, To) -- Check that cargo weight is if self:CanCargo(cargo.opsgroup) and (not (cargo.delivered or cargo.opsgroup:IsDead())) then + + -- Check if cargo is currently acting as carrier. + local isCarrier=cargo.opsgroup:IsPickingup() or cargo.opsgroup:IsLoading() or cargo.opsgroup:IsTransporting() or cargo.opsgroup:IsUnloading() -- Check that group is NOT cargo and NOT acting as carrier already -- TODO: Need a better :IsBusy() function or :IsReadyForMission() :IsReadyForBoarding() :IsReadyForTransport() - if cargo.opsgroup:IsNotCargo(true) and not (cargo.opsgroup:IsPickingup() or cargo.opsgroup:IsLoading() or cargo.opsgroup:IsTransporting() or cargo.opsgroup:IsUnloading()) then + if cargo.opsgroup:IsNotCargo(true) and not isCarrier then -- Check if cargo is in embark/pickup zone. local inzone=cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone) @@ -6734,7 +6745,7 @@ function OPSGROUP:onafterTransport(From, Event, To) end -- Start unloading. - self:__UnLoading(-5) + self:__Unloading(-5) else @@ -6780,7 +6791,7 @@ function OPSGROUP:onafterTransport(From, Event, To) --- -- If this is a helo and no ZONE_AIRBASE was given, we make the helo land in the pickup zone. - local waypoint=FLIGHTGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid, self.altitudeCruise, false) ; waypoint.detour=1 + local waypoint=FLIGHTGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid, UTILS.MetersToFeet(self.altitudeCruise), false) ; waypoint.detour=1 -- Cancel landedAt task. This should trigger Cruise once airborne. if self:IsFlightgroup() and self:IsLandedAt() then @@ -6916,13 +6927,17 @@ function OPSGROUP:onafterUnloading(From, Event, To) self:Unload(cargo.opsgroup) else + + -- Get disembark zone of this TZC. + local DisembarkZone=self.cargoTransport:GetDisembarkZone(self.cargoTZC) local Coordinate=nil + - if self.cargoTransport:GetDisembarkZone(self.cargoTZC) then + if DisembarkZone then -- Random coordinate in disembark zone. - Coordinate=self.cargoTransport:GetDisembarkZone(self.cargoTZC):GetRandomCoordinate() + Coordinate=DisembarkZone:GetRandomCoordinate() else diff --git a/Moose Development/Moose/Ops/OpsTransport.lua b/Moose Development/Moose/Ops/OpsTransport.lua index cd9357650..48bbb0922 100644 --- a/Moose Development/Moose/Ops/OpsTransport.lua +++ b/Moose Development/Moose/Ops/OpsTransport.lua @@ -173,7 +173,7 @@ _OPSTRANSPORTID=0 --- Army Group version. -- @field #string version -OPSTRANSPORT.version="0.4.0" +OPSTRANSPORT.version="0.4.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -243,6 +243,130 @@ function OPSTRANSPORT:New(CargoGroups, PickupZone, DeployZone) self:AddTransition("*", "DeadCarrierUnit", "*") self:AddTransition("*", "DeadCarrierGroup", "*") self:AddTransition("*", "DeadCarrierAll", "*") + + ------------------------ + --- Pseudo Functions --- + ------------------------ + + --- Triggers the FSM event "Status". + -- @function [parent=#OPSTRANSPORT] Status + -- @param #OPSTRANSPORT self + + --- Triggers the FSM event "Status" after a delay. + -- @function [parent=#OPSTRANSPORT] __Status + -- @param #OPSTRANSPORT self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Planned". + -- @function [parent=#OPSTRANSPORT] Planned + -- @param #OPSTRANSPORT self + + --- Triggers the FSM event "Planned" after a delay. + -- @function [parent=#OPSTRANSPORT] __Planned + -- @param #OPSTRANSPORT self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Queued". + -- @function [parent=#OPSTRANSPORT] Queued + -- @param #OPSTRANSPORT self + + --- Triggers the FSM event "Queued" after a delay. + -- @function [parent=#OPSTRANSPORT] __Queued + -- @param #OPSTRANSPORT self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Requested". + -- @function [parent=#OPSTRANSPORT] Requested + -- @param #OPSTRANSPORT self + + --- Triggers the FSM event "Requested" after a delay. + -- @function [parent=#OPSTRANSPORT] __Requested + -- @param #OPSTRANSPORT self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Scheduled". + -- @function [parent=#OPSTRANSPORT] Scheduled + -- @param #OPSTRANSPORT self + + --- Triggers the FSM event "Scheduled" after a delay. + -- @function [parent=#OPSTRANSPORT] __Scheduled + -- @param #OPSTRANSPORT self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Executing". + -- @function [parent=#OPSTRANSPORT] Executing + -- @param #OPSTRANSPORT self + + --- Triggers the FSM event "Executing" after a delay. + -- @function [parent=#OPSTRANSPORT] __Executing + -- @param #OPSTRANSPORT self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Delivered". + -- @function [parent=#OPSTRANSPORT] Delivered + -- @param #OPSTRANSPORT self + + --- Triggers the FSM event "Delivered" after a delay. + -- @function [parent=#OPSTRANSPORT] __Delivered + -- @param #OPSTRANSPORT self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Loaded". + -- @function [parent=#OPSTRANSPORT] Loaded + -- @param #OPSTRANSPORT self + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCargo OPSGROUP that was loaded into a carrier. + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCarrier OPSGROUP that was loaded into a carrier. + -- @param Ops.OpsGroup#OPSGROUP.Element CarrierElement Carrier element. + + --- Triggers the FSM event "Loaded" after a delay. + -- @function [parent=#OPSTRANSPORT] __Loaded + -- @param #OPSTRANSPORT self + -- @param #number delay Delay in seconds. + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCargo OPSGROUP that was loaded into a carrier. + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCarrier OPSGROUP that was loaded into a carrier. + -- @param Ops.OpsGroup#OPSGROUP.Element CarrierElement Carrier element. + + --- On after "Loaded" event. + -- @function [parent=#OPSTRANSPORT] OnAfterLoaded + -- @param #OPSGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCargo OPSGROUP that was loaded into a carrier. + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCarrier OPSGROUP that was loaded into a carrier. + -- @param Ops.OpsGroup#OPSGROUP.Element CarrierElement Carrier element. + + + --- Triggers the FSM event "Unloaded". + -- @function [parent=#OPSTRANSPORT] Unloaded + -- @param #OPSTRANSPORT self + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCargo Cargo OPSGROUP that was unloaded from a carrier. + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCarrier Carrier OPSGROUP that unloaded the cargo. + + --- Triggers the FSM event "Unloaded" after a delay. + -- @function [parent=#OPSTRANSPORT] __Unloaded + -- @param #OPSTRANSPORT self + -- @param #number delay Delay in seconds. + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCargo Cargo OPSGROUP that was unloaded from a carrier. + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCarrier Carrier OPSGROUP that unloaded the cargo. + + + --- On after "Unloaded" event. + -- @function [parent=#OPSTRANSPORT] OnAfterUnloaded + -- @param #OPSGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCargo Cargo OPSGROUP that was unloaded from a carrier. + -- @param Ops.OpsGroup#OPSGROUP OpsGroupCarrier Carrier OPSGROUP that unloaded the cargo. + --TODO: Psydofunctions @@ -1146,9 +1270,18 @@ end --- Check if all cargo was delivered (or is dead). -- @param #OPSTRANSPORT self +-- @param #number Nmin Number of groups that must be actually delivered (and are not dead). Default 0. -- @return #boolean If true, all possible cargo was delivered. -function OPSTRANSPORT:IsDelivered() - return self:is(OPSTRANSPORT.Status.DELIVERED) +function OPSTRANSPORT:IsDelivered(Nmin) + local is=self:is(OPSTRANSPORT.Status.DELIVERED) + Nmin=Nmin or 0 + if Nmin>self.Ncargo then + Nmin=self.Ncargo + end + if self.Ndelivered