OPSTRANSPORT v0.4

- Multiple transport zone combos (TZC)
- Other issues solved.
- Demo miz D-day landing is still not working correctly.
This commit is contained in:
Frank 2021-08-30 23:45:40 +02:00
parent 28ddfa5243
commit 0b1e25b073
6 changed files with 625 additions and 294 deletions

View File

@ -6329,6 +6329,10 @@ function WAREHOUSE:_OnEventBirth(EventData)
local request=self:GetRequestByID(rid) local request=self:GetRequestByID(rid)
if asset and request then if asset and request then
if asset.spawned and type(asset.spawned)=="boolean" and asset.spawned==true then
return
end
-- Debug message. -- Debug message.
self:T(self.lid..string.format("Warehouse %s captured event birth of request ID=%d, asset ID=%d, unit %s spawned=%s", self.alias, request.uid, asset.uid, EventData.IniUnitName, tostring(asset.spawned))) self:T(self.lid..string.format("Warehouse %s captured event birth of request ID=%d, asset ID=%d, unit %s spawned=%s", self.alias, request.uid, asset.uid, EventData.IniUnitName, tostring(asset.spawned)))

View File

@ -90,10 +90,7 @@ function ARMYGROUP:New(group)
og:I(og.lid..string.format("WARNING: OPS group already exists in data base!")) og:I(og.lid..string.format("WARNING: OPS group already exists in data base!"))
return og return og
end end
-- First set ARMYGROUP.
self.isArmygroup=true
-- Inherit everything from FSM class. -- Inherit everything from FSM class.
local self=BASE:Inherit(self, OPSGROUP:New(group)) -- #ARMYGROUP local self=BASE:Inherit(self, OPSGROUP:New(group)) -- #ARMYGROUP
@ -595,6 +592,16 @@ end
-- @param #number Formation Formation of the group. -- @param #number Formation Formation of the group.
function ARMYGROUP:onbeforeUpdateRoute(From, Event, To, n, Speed, Formation) function ARMYGROUP:onbeforeUpdateRoute(From, Event, To, n, Speed, Formation)
if self:IsWaiting() then if self:IsWaiting() then
self:E(self.lid.."Update route denied. Group is WAIRING!")
return false
elseif self:IsInUtero() then
self:E(self.lid.."Update route denied. Group is INUTERO!")
return false
elseif self:IsDead() then
self:E(self.lid.."Update route denied. Group is DEAD!")
return false
elseif self:IsStopped() then
self:E(self.lid.."Update route denied. Group is STOPPED!")
return false return false
end end
return true return true

View File

@ -32,7 +32,7 @@
--- FLIGHTGROUP class. --- FLIGHTGROUP class.
-- @type FLIGHTGROUP -- @type FLIGHTGROUP
-- @field #string actype Type name of the aircraft. -- @field #string actype Type name of the aircraft.
-- @field #number rangemax Max range in km. -- @field #number rangemax Max range in meters.
-- @field #number ceiling Max altitude the aircraft can fly at in meters. -- @field #number ceiling Max altitude the aircraft can fly at in meters.
-- @field #number tankertype The refueling system type (0=boom, 1=probe), if the group is a tanker. -- @field #number tankertype The refueling system type (0=boom, 1=probe), if the group is a tanker.
-- @field #number refueltype The refueling system type (0=boom, 1=probe), if the group can refuel from a tanker. -- @field #number refueltype The refueling system type (0=boom, 1=probe), if the group can refuel from a tanker.
@ -208,9 +208,6 @@ function FLIGHTGROUP:New(group)
return og return og
end end
-- First set FLIGHTGROUP.
self.isFlightgroup=true
-- Inherit everything from FSM class. -- Inherit everything from FSM class.
local self=BASE:Inherit(self, OPSGROUP:New(group)) -- #FLIGHTGROUP local self=BASE:Inherit(self, OPSGROUP:New(group)) -- #FLIGHTGROUP
@ -1766,14 +1763,12 @@ function FLIGHTGROUP:onafterCruise(From, Event, To)
--- ---
if self:IsTransporting() then if self:IsTransporting() then
if self.cargoTransport and self.cargoTransport.deployzone and self.cargoTransport.deployzone:IsInstanceOf("ZONE_AIRBASE") then if self.cargoTransport and self.cargoTZC and self.cargoTZC.DeployAirbase then
local airbase=self.cargoTransport.deployzone:GetAirbase() self:LandAtAirbase(self.cargoTZC.DeployAirbase)
self:LandAtAirbase(airbase)
end end
elseif self:IsPickingup() then elseif self:IsPickingup() then
if self.cargoTransport and self.cargoTransport.pickupzone and self.cargoTransport.pickupzone:IsInstanceOf("ZONE_AIRBASE") then if self.cargoTransport and self.cargoTZC and self.cargoTZC.PickupAirbase then
local airbase=self.cargoTransport.pickupzone:GetAirbase() self:LandAtAirbase(self.cargoTZC.PickupAirbase)
self:LandAtAirbase(airbase)
end end
else else
self:_CheckGroupDone(nil, 120) self:_CheckGroupDone(nil, 120)
@ -1983,6 +1978,9 @@ function FLIGHTGROUP:onbeforeUpdateRoute(From, Event, To, n)
-- Group is dead! No more updates. -- Group is dead! No more updates.
self:E(self.lid.."Update route denied. Group is DEAD!") self:E(self.lid.."Update route denied. Group is DEAD!")
allowed=false allowed=false
elseif self:IsInUtero() then
self:E(self.lid.."Update route denied. Group is INUTERO!")
allowed=false
else else
-- Not airborne yet. Try again in 5 sec. -- Not airborne yet. Try again in 5 sec.
self:T(self.lid.."Update route denied ==> checking back in 5 sec") self:T(self.lid.."Update route denied ==> checking back in 5 sec")
@ -2084,9 +2082,9 @@ function FLIGHTGROUP:onafterUpdateRoute(From, Event, To, n)
local waypointAction=COORDINATE.WaypointAction.TurningPoint local waypointAction=COORDINATE.WaypointAction.TurningPoint
if self:IsLanded() or self:IsLandedAt() or self:IsAirborne()==false then if self:IsLanded() or self:IsLandedAt() or self:IsAirborne()==false then
-- Had some issues with passing waypoint function of the next WP called too ealy when the type is TurningPoint. Setting it to TakeOff solved it! -- Had some issues with passing waypoint function of the next WP called too ealy when the type is TurningPoint. Setting it to TakeOff solved it!
--waypointType=COORDINATE.WaypointType.TakeOff waypointType=COORDINATE.WaypointType.TakeOff
waypointType=COORDINATE.WaypointType.TakeOffGroundHot --waypointType=COORDINATE.WaypointType.TakeOffGroundHot
waypointAction=COORDINATE.WaypointAction.FromGroundAreaHot --waypointAction=COORDINATE.WaypointAction.FromGroundAreaHot
end end
-- Set current waypoint or we get problem that the _PassingWaypoint function is triggered too early, i.e. right now and not when passing the next WP. -- Set current waypoint or we get problem that the _PassingWaypoint function is triggered too early, i.e. right now and not when passing the next WP.
@ -2585,9 +2583,9 @@ function FLIGHTGROUP:onbeforeWait(From, Event, To, Duration, Altitude, Speed)
-- Check for a current transport assignment. -- Check for a current transport assignment.
if self.cargoTransport and not self:IsLandedAt() then if self.cargoTransport and not self:IsLandedAt() then
self:I(self.lid..string.format("WARNING: Got current TRANSPORT assignment ==> WAIT event is suspended for 30 sec!")) --self:I(self.lid..string.format("WARNING: Got current TRANSPORT assignment ==> WAIT event is suspended for 30 sec!"))
Tsuspend=-30 --Tsuspend=-30
allowed=false --allowed=false
end end
-- Call wait again. -- Call wait again.

View File

@ -114,9 +114,6 @@ function NAVYGROUP:New(group)
return og return og
end end
-- First set NAVYGROUP.
self.isNavygroup=true
-- Inherit everything from FSM class. -- Inherit everything from FSM class.
local self=BASE:Inherit(self, OPSGROUP:New(group)) -- #NAVYGROUP local self=BASE:Inherit(self, OPSGROUP:New(group)) -- #NAVYGROUP
@ -467,6 +464,9 @@ function NAVYGROUP:Status(From, Event, To)
-- Is group alive? -- Is group alive?
local alive=self:IsAlive() local alive=self:IsAlive()
--
local freepath=0
if alive then if alive then
--- ---
@ -485,7 +485,7 @@ function NAVYGROUP:Status(From, Event, To)
self:_CheckTurning() self:_CheckTurning()
local disttoWP=math.min(self:GetDistanceToWaypoint(), UTILS.NMToMeters(10)) local disttoWP=math.min(self:GetDistanceToWaypoint(), UTILS.NMToMeters(10))
local freepath=disttoWP freepath=disttoWP
-- Only check if not currently turning. -- Only check if not currently turning.
if not self:IsTurning() then if not self:IsTurning() then
@ -713,6 +713,16 @@ end
-- @param #number Depth Depth in meters to the next waypoint. -- @param #number Depth Depth in meters to the next waypoint.
function NAVYGROUP:onbeforeUpdateRoute(From, Event, To, n, Speed, Depth) function NAVYGROUP:onbeforeUpdateRoute(From, Event, To, n, Speed, Depth)
if self:IsWaiting() then if self:IsWaiting() then
self:E(self.lid.."Update route denied. Group is WAIRING!")
return false
elseif self:IsInUtero() then
self:E(self.lid.."Update route denied. Group is INUTERO!")
return false
elseif self:IsDead() then
self:E(self.lid.."Update route denied. Group is DEAD!")
return false
elseif self:IsStopped() then
self:E(self.lid.."Update route denied. Group is STOPPED!")
return false return false
end end
return true return true
@ -771,7 +781,7 @@ function NAVYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Depth)
table.insert(waypoints, 1, current) table.insert(waypoints, 1, current)
if not self.passedfinalwp then if self:IsEngaging() or not self.passedfinalwp then
-- Debug info. -- Debug info.
self:T(self.lid..string.format("Updateing route: WP %d-->%d (%d/%d), Speed=%.1f knots, Depth=%d m", self.currentwp, n, #waypoints, #self.waypoints, UTILS.MpsToKnots(self.speedWp), wp.alt)) self:T(self.lid..string.format("Updateing route: WP %d-->%d (%d/%d), Speed=%.1f knots, Depth=%d m", self.currentwp, n, #waypoints, #self.waypoints, UTILS.MpsToKnots(self.speedWp), wp.alt))

View File

@ -115,7 +115,7 @@
-- @field #table cargoqueue Table containing cargo groups to be transported. -- @field #table cargoqueue Table containing cargo groups to be transported.
-- @field #table cargoBay Table containing OPSGROUP loaded into this group. -- @field #table cargoBay Table containing OPSGROUP loaded into this group.
-- @field Ops.OpsTransport#OPSTRANSPORT cargoTransport Current cargo transport assignment. -- @field Ops.OpsTransport#OPSTRANSPORT cargoTransport Current cargo transport assignment.
-- @field Ops.OpsTransport#OPSTRANSPORT.TransportZone transportZone Transport zones (pickup, deploy etc.). -- @field Ops.OpsTransport#OPSTRANSPORT.TransportZoneCombo cargoTZC Transport zone combo (pickup, deploy etc.) currently used.
-- @field #string cargoStatus Cargo status of this group acting as cargo. -- @field #string cargoStatus Cargo status of this group acting as cargo.
-- @field #number cargoTransportUID Unique ID of the transport assignment this cargo group is associated with. -- @field #number cargoTransportUID Unique ID of the transport assignment this cargo group is associated with.
-- @field #string carrierStatus Carrier status of this group acting as cargo carrier. -- @field #string carrierStatus Carrier status of this group acting as cargo carrier.
@ -512,6 +512,8 @@ function OPSGROUP:New(group)
end end
end end
self.group:IsAir()
-- Set the template. -- Set the template.
self:_SetTemplate() self:_SetTemplate()
@ -519,7 +521,27 @@ function OPSGROUP:New(group)
self.dcsgroup=self:GetDCSGroup() self.dcsgroup=self:GetDCSGroup()
self.controller=self.dcsgroup:getController() self.controller=self.dcsgroup:getController()
-- Category.
self.category=self.dcsgroup:getCategory()
if self.category==Group.Category.GROUND then
self.isArmygroup=true
elseif self.category==Group.Category.TRAIN then
self.isArmygroup=true
self.isTrain=true
elseif self.category==Group.Category.SHIP then
self.isNavygroup=true
-- TODO submarine
elseif self.category==Group.Category.AIRPLANE then
self.isFlightgroup=true
elseif self.category==Group.Category.HELICOPTER then
self.isFlightgroup=true
self.isHelo=true
else
end
local units=self.group:GetUnits() local units=self.group:GetUnits()
if units then if units then
local masterunit=units[1] --Wrapper.Unit#UNIT local masterunit=units[1] --Wrapper.Unit#UNIT
@ -531,10 +553,8 @@ function OPSGROUP:New(group)
if self:IsFlightgroup() then if self:IsFlightgroup() then
self.rangemax=masterunit:GetRange() self.rangemax=self.descriptors.range and self.descriptors.range*1000 or 500*1000
self.descriptors=masterunit:GetDesc()
self.ceiling=self.descriptors.Hmax self.ceiling=self.descriptors.Hmax
self.tankertype=select(2, masterunit:IsTanker()) self.tankertype=select(2, masterunit:IsTanker())
@ -1783,7 +1803,10 @@ end
-- @return #boolean If true, group is in this zone -- @return #boolean If true, group is in this zone
function OPSGROUP:IsInZone(Zone) function OPSGROUP:IsInZone(Zone)
local vec2=self:GetVec2() local vec2=self:GetVec2()
local is=Zone:IsVec2InZone(vec2) local is=false
if vec2 then
is=Zone:IsVec2InZone(vec2)
end
return is return is
end end
@ -2750,17 +2773,33 @@ function OPSGROUP:PushTask(DCSTask)
return self return self
end end
--- Returns true if the DCS controller currently has a task.
-- @param #OPSGROUP self
-- @param #number Delay Delay in seconds.
-- @return #boolean True or false if the controller has a task. Nil if no controller.
function OPSGROUP:HasTaskController(Delay)
local hastask=nil
if self.controller then
hastask=self.controller:hasTask()
end
self:I(self.lid..string.format("Controller hasTask=%s", tostring(hastask)))
return hastask
end
--- Clear DCS tasks. --- Clear DCS tasks.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @return #OPSGROUP self -- @return #OPSGROUP self
function OPSGROUP:ClearTasks() function OPSGROUP:ClearTasks()
if self:IsAlive() then local hastask=self:HasTaskController()
if self:IsAlive() and self.controller and self:HasTaskController() then
self:I(self.lid..string.format("CLEARING Tasks")) self:I(self.lid..string.format("CLEARING Tasks"))
self.group:ClearTasks() self.controller:resetTask()
end end
return self return self
end end
--- Add a *scheduled* task. --- Add a *scheduled* task.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #table task DCS task table structure. -- @param #table task DCS task table structure.
@ -5262,7 +5301,11 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
self:T({Template=Template}) self:T({Template=Template})
-- Spawn new group. -- Spawn new group.
_DATABASE:Spawn(Template) self.group=_DATABASE:Spawn(Template)
-- Set DCS group and controller.
self.dcsgroup=self:GetDCSGroup()
self.controller=self.dcsgroup:getController()
-- Set activation and controlled state. -- Set activation and controlled state.
self.isLateActivated=Template.lateActivation self.isLateActivated=Template.lateActivation
@ -5373,6 +5416,7 @@ function OPSGROUP:onafterDead(From, Event, To)
-- No current cargo transport. -- No current cargo transport.
self.cargoTransport=nil self.cargoTransport=nil
self.cargoTZC=nil
if self.Ndestroyed==#self.elements then if self.Ndestroyed==#self.elements then
if self.cohort then if self.cohort then
@ -5488,10 +5532,11 @@ function OPSGROUP:_CheckCargoTransport()
if self.verbose>=3 then if self.verbose>=3 then
local text="" local text=""
for i,_transport in pairs(self.cargoqueue) do for i,_transport in pairs(self.cargoqueue) do
local transport=_transport --#Ops.OpsTransport#OPSTRANSPORT local transport=_transport --#Ops.OpsTransport#OPSTRANSPORT
local pickupzone=transport:GetPickupZone()
local pickupname=transport.pickupzone and transport.pickupzone:GetName() or "unknown" local deployzone=transport:GetDeployZone()
local deployname=transport.deployzone and transport.deployzone:GetName() or "unknown" local pickupname=pickupzone and pickupzone:GetName() or "unknown"
local deployname=deployzone and deployzone:GetName() or "unknown"
text=text..string.format("\n[%d] UID=%d Status=%s: %s --> %s", i, transport.uid, transport:GetState(), pickupname, deployname) text=text..string.format("\n[%d] UID=%d Status=%s: %s --> %s", i, transport.uid, transport:GetState(), pickupname, deployname)
for j,_cargo in pairs(transport.cargos) do for j,_cargo in pairs(transport.cargos) do
local cargo=_cargo --#OPSGROUP.CargoGroup local cargo=_cargo --#OPSGROUP.CargoGroup
@ -5520,38 +5565,18 @@ function OPSGROUP:_CheckCargoTransport()
-- Now handle the transport. -- Now handle the transport.
if self.cargoTransport then if self.cargoTransport then
-- Debug info.
if self.verbose>=2 then
local pickupname=self.cargoTransport.pickupzone and self.cargoTransport.pickupzone:GetName() or "unknown"
local deployname=self.cargoTransport.deployzone and self.cargoTransport.deployzone:GetName() or "unknown"
local text=string.format("Carrier [%s]: %s --> %s", self.carrierStatus, pickupname, deployname)
for _,_cargo in pairs(self.cargoTransport.cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
local name=cargo.opsgroup:GetName()
local gstatus=cargo.opsgroup:GetState()
local cstatus=cargo.opsgroup.cargoStatus
local weight=cargo.opsgroup:GetWeightTotal()
local carriergroup, carrierelement, reserved=cargo.opsgroup:_GetMyCarrier()
local carrierGroupname=carriergroup and carriergroup.groupname or "none"
local carrierElementname=carrierelement and carrierelement.name or "none"
text=text..string.format("\n- %s (%.1f kg) [%s]: %s, carrier=%s (%s), delivered=%s", name, weight, gstatus, cstatus, carrierElementname, carrierGroupname, tostring(cargo.delivered))
end
self:I(self.lid..text)
end
if self:IsNotCarrier() then if self:IsNotCarrier() then
-- Debug info. -- Debug info.
self:T(self.lid.."Not carrier ==> pickup?") self:T(self.lid.."Not carrier ==> pickup?")
-- Get transport zones. -- Get transport zone combo (TZC).
local transportZone=self.cargoTransport:_GetPickupZone(self) self.cargoTZC=self.cargoTransport:_GetTransportZoneCombo(self)
if transportZone then if self.cargoTZC then
-- Initiate the cargo transport process. -- Initiate the cargo transport process.
self:__Pickup(-1, transportZone) self:__Pickup(-1)
else else
self:T(self.lid.."Not carrier ==> pickup") self:T(self.lid.."Not carrier ==> pickup")
@ -5573,7 +5598,7 @@ function OPSGROUP:_CheckCargoTransport()
local boarding=false local boarding=false
local gotcargo=false local gotcargo=false
for _,_cargo in pairs(self.cargoTransport.cargos) do for _,_cargo in pairs(self.cargoTZC.Cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
-- Check if anyone is still boarding. -- Check if anyone is still boarding.
@ -5589,7 +5614,7 @@ function OPSGROUP:_CheckCargoTransport()
end end
-- Boarding finished ==> Transport cargo. -- Boarding finished ==> Transport cargo.
if gotcargo and self.cargoTransport:_CheckRequiredCargos() and not boarding then if gotcargo and self.cargoTransport:_CheckRequiredCargos(self.cargoTZC) and not boarding then
self:T(self.lid.."Boarding finished ==> Loaded") self:T(self.lid.."Boarding finished ==> Loaded")
self:Loaded() self:Loaded()
else else
@ -5613,7 +5638,7 @@ function OPSGROUP:_CheckCargoTransport()
self:T(self.lid.."Unloading ==> Checking if all cargo was delivered") self:T(self.lid.."Unloading ==> Checking if all cargo was delivered")
local delivered=true local delivered=true
for _,_cargo in pairs(self.cargoTransport.cargos) do for _,_cargo in pairs(self.cargoTZC.Cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
local carrierGroup=cargo.opsgroup:_GetMyCarrierGroup() local carrierGroup=cargo.opsgroup:_GetMyCarrierGroup()
@ -5629,12 +5654,33 @@ function OPSGROUP:_CheckCargoTransport()
-- Unloading finished ==> pickup next batch or call it a day. -- Unloading finished ==> pickup next batch or call it a day.
if delivered then if delivered then
self:T(self.lid.."Unloading finished ==> UnloadingDone") self:T(self.lid.."Unloading finished ==> UnloadingDone")
self:UnloadingDone() self:__UnloadingDone(10)
else else
self:Unloading() self:Unloading()
end end
end end
-- Debug info. (At this point, we might not have a current cargo transport ==> hence the check)
if self.verbose>=2 and self.cargoTransport then
local pickupzone=self.cargoTransport:GetPickupZone(self.cargoTZC)
local deployzone=self.cargoTransport:GetDeployZone(self.cargoTZC)
local pickupname=pickupzone and pickupzone:GetName() or "unknown"
local deployname=deployzone and deployzone:GetName() or "unknown"
local text=string.format("Carrier [%s]: %s --> %s", self.carrierStatus, pickupname, deployname)
for _,_cargo in pairs(self.cargoTransport:GetCargos(self.cargoTZC)) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
local name=cargo.opsgroup:GetName()
local gstatus=cargo.opsgroup:GetState()
local cstatus=cargo.opsgroup.cargoStatus
local weight=cargo.opsgroup:GetWeightTotal()
local carriergroup, carrierelement, reserved=cargo.opsgroup:_GetMyCarrier()
local carrierGroupname=carriergroup and carriergroup.groupname or "none"
local carrierElementname=carrierelement and carrierelement.name or "none"
text=text..string.format("\n- %s (%.1f kg) [%s]: %s, carrier=%s (%s), delivered=%s", name, weight, gstatus, cstatus, carrierElementname, carrierGroupname, tostring(cargo.delivered))
end
self:I(self.lid..text)
end
end end
@ -5851,28 +5897,33 @@ end
function OPSGROUP:_CheckGoPickup(CargoTransport) function OPSGROUP:_CheckGoPickup(CargoTransport)
local done=true local done=true
for _,_cargo in pairs(CargoTransport.cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup if CargoTransport then
if self:CanCargo(cargo.opsgroup) then for _,_cargo in pairs(CargoTransport.cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if cargo.delivered then
-- This one is delivered. if self:CanCargo(cargo.opsgroup) then
elseif cargo.opsgroup==nil or cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped() then
-- This one is dead. if cargo.delivered then
elseif cargo.opsgroup:IsLoaded(CargoTransport:_GetCarrierNames()) then -- This one is delivered.
-- This one is loaded into a(nother) carrier. elseif cargo.opsgroup==nil or cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped() then
else -- This one is dead.
done=false --Someone is not done! elseif cargo.opsgroup:IsLoaded(CargoTransport:_GetCarrierNames()) then
-- This one is loaded into a(nother) carrier.
else
done=false --Someone is not done!
end
end end
end end
-- Debug info.
self:T(self.lid..string.format("Cargotransport UID=%d Status=%s: delivered=%s", CargoTransport.uid, CargoTransport:GetState(), tostring(done)))
end end
-- Debug info.
self:T(self.lid..string.format("Cargotransport UID=%d Status=%s: delivered=%s", CargoTransport.uid, CargoTransport:GetState(), tostring(done)))
return done return done
end end
@ -6067,7 +6118,7 @@ function OPSGROUP:GetWeightCargo(UnitName, IncludeReserved)
end end
-- Debug info. -- Debug info.
self:T2(self.lid..string.format("Unit=%s (reserved=%s): weight=%d, gewicht=%d", tostring(UnitName), tostring(IncludeReserved), weight, gewicht)) self:T3(self.lid..string.format("Unit=%s (reserved=%s): weight=%d, gewicht=%d", tostring(UnitName), tostring(IncludeReserved), weight, gewicht))
-- Quick check. -- Quick check.
if IncludeReserved==false and gewicht~=weight then if IncludeReserved==false and gewicht~=weight then
@ -6281,29 +6332,29 @@ end
-- @param #string From From state. -- @param #string From From state.
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
-- @param Ops.OpsTransport#OPSTRANSPORT.TransportZone TransportZone The transport zones data table. function OPSGROUP:onafterPickup(From, Event, To)
function OPSGROUP:onafterPickup(From, Event, To, TransportZone)
-- Set carrier status. -- Set carrier status.
self:_NewCarrierStatus(OPSGROUP.CarrierStatus.PICKUP) self:_NewCarrierStatus(OPSGROUP.CarrierStatus.PICKUP)
self.transportZone=TransportZone local TZC=self.cargoTZC
-- Pickup zone. -- Pickup zone.
local Zone=TransportZone.PickupZone local Zone=TZC.PickupZone
-- Check if already in the pickup zone. -- Check if already in the pickup zone.
local inzone=self:IsInZone(Zone) local inzone=self:IsInZone(Zone)
local airbasePickup=nil --Wrapper.Airbase#AIRBASE -- Pickup at an airbase.
if Zone and Zone:IsInstanceOf("ZONE_AIRBASE") then local airbasePickup=TZC.PickupAirbase --Wrapper.Airbase#AIRBASE
airbasePickup=Zone:GetAirbase()
end
-- Check if group is already ready for loading. -- Check if group is already ready for loading.
local ready4loading=false local ready4loading=false
if self:IsArmygroup() or self:IsNavygroup() then if self:IsArmygroup() or self:IsNavygroup() then
-- Army and Navy groups just need to be inside the zone.
ready4loading=inzone ready4loading=inzone
else else
-- Aircraft is already parking at the pickup airbase. -- Aircraft is already parking at the pickup airbase.
@ -6315,6 +6366,7 @@ function OPSGROUP:onafterPickup(From, Event, To, TransportZone)
end end
end end
-- Ready for loading?
if ready4loading then if ready4loading then
-- We are already in the pickup zone ==> wait and initiate loading. -- We are already in the pickup zone ==> wait and initiate loading.
@ -6350,16 +6402,14 @@ function OPSGROUP:onafterPickup(From, Event, To, TransportZone)
-- Activate uncontrolled group. -- Activate uncontrolled group.
if self:IsParking() and self:IsUncontrolled() then if self:IsParking() and self:IsUncontrolled() then
self:StartUncontrolled(1) self:StartUncontrolled()
end end
else
-- Order group to land at an airbase.
self:LandAtAirbase(airbasePickup)
end end
-- Order group to land at an airbase.
self:__LandAtAirbase(-0.5, airbasePickup)
elseif self.isHelo then elseif self.isHelo then
--- ---
@ -6375,10 +6425,10 @@ function OPSGROUP:onafterPickup(From, Event, To, TransportZone)
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, self.altitudeCruise, false) ; waypoint.detour=1
else else
self:E(self.lid.."ERROR: Carrier aircraft cannot land in Pickup zone! Specify a ZONE_AIRBASE as pickup zone") self:E(self.lid.."ERROR: Transportcarrier aircraft cannot land in Pickup zone! Specify a ZONE_AIRBASE as pickup zone")
end end
elseif self.isNavygroup then elseif self:IsNavygroup() then
--- ---
-- Navy Group -- Navy Group
@ -6388,7 +6438,7 @@ function OPSGROUP:onafterPickup(From, Event, To, TransportZone)
local uid=cwp and cwp.uid or nil local uid=cwp and cwp.uid or nil
-- Get a (random) pre-defined transport path. -- Get a (random) pre-defined transport path.
local path=self.cargoTransport:_GetPathPickup() local path=self.cargoTransport:_GetPathPickup(self.cargoTZC)
if path then if path then
-- Loop over coordinates. -- Loop over coordinates.
@ -6406,7 +6456,7 @@ function OPSGROUP:onafterPickup(From, Event, To, TransportZone)
self:__Cruise(-2) self:__Cruise(-2)
elseif self.isArmygroup then elseif self:IsArmygroup() then
--- ---
-- Army Group -- Army Group
@ -6416,14 +6466,13 @@ function OPSGROUP:onafterPickup(From, Event, To, TransportZone)
local uid=cwp and cwp.uid or nil local uid=cwp and cwp.uid or nil
-- Get a (random) pre-defined transport path. -- Get a (random) pre-defined transport path.
local path=self.cargoTransport:_GetPathPickup() local path=self.cargoTransport:_GetPathPickup(self.cargoTZC)
if path then if path then
-- Loop over coordinates. -- Loop over coordinates.
for i,coordinate in pairs(path) do for i,coordinate in pairs(path) do
local waypoint=ARMYGROUP.AddWaypoint(self, coordinate, nil, uid) ; waypoint.temp=true local waypoint=ARMYGROUP.AddWaypoint(self, coordinate, nil, uid) ; waypoint.temp=true
uid=waypoint.uid uid=waypoint.uid
--coordinate:MarkToAll(string.format("Path i=%d, UID=%d", i, uid))
end end
end end
@ -6455,8 +6504,11 @@ function OPSGROUP:onafterLoading(From, Event, To)
--TODO: sort cargos wrt weight. --TODO: sort cargos wrt weight.
-- Cargo group table.
local cargos=self.cargoTZC.Cargos
-- Loop over all cargos. -- Loop over all cargos.
for _,_cargo in pairs(self.cargoTransport.cargos) do for _,_cargo in pairs(cargos) do
local cargo=_cargo --#OPSGROUP.CargoGroup local cargo=_cargo --#OPSGROUP.CargoGroup
-- Check that cargo weight is -- Check that cargo weight is
@ -6467,8 +6519,7 @@ function OPSGROUP:onafterLoading(From, Event, To)
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 (cargo.opsgroup:IsPickingup() or cargo.opsgroup:IsLoading() or cargo.opsgroup:IsTransporting() or cargo.opsgroup:IsUnloading()) then
-- Check if cargo is in embark/pickup zone. -- Check if cargo is in embark/pickup zone.
--local inzone=self.cargoTransport.embarkzone:IsCoordinateInZone(cargo.opsgroup:GetCoordinate()) local inzone=cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone)
local inzone=cargo.opsgroup:IsInZone(self.transportZone.EmbarkZone)
-- Cargo MUST be inside zone or it will not be loaded! -- Cargo MUST be inside zone or it will not be loaded!
if inzone then if inzone then
@ -6486,12 +6537,12 @@ function OPSGROUP:onafterLoading(From, Event, To)
else else
-- Debug info. -- Debug info.
self:T(self.lid..string.format("Cannot board carrier! Group %s is NOT (yet) in zone %s", cargo.opsgroup:GetName(), self.cargoTransport.embarkzone:GetName())) self:T(self.lid..string.format("Cannot board carrier! Group %s is NOT (yet) in zone %s", cargo.opsgroup:GetName(), self.cargoTZC.EmbarkZone:GetName()))
end end
else else
-- Debug info. -- Debug info.
self:T(self.lid..string.format("Cargo %s NOT in embark zone %s", cargo.opsgroup:GetName(), self.cargoTransport.embarkzone:GetName())) self:T(self.lid..string.format("Cargo %s NOT in embark zone %s", cargo.opsgroup:GetName(), self.cargoTZC.EmbarkZone:GetName()))
end end
end end
@ -6653,15 +6704,13 @@ function OPSGROUP:onafterTransport(From, Event, To)
--TODO: This is all very similar to the onafterPickup() function. Could make it general. --TODO: This is all very similar to the onafterPickup() function. Could make it general.
-- Deploy zone. -- Deploy zone.
local Zone=self.transportZone.DeployZone local Zone=self.cargoTZC.DeployZone
-- Check if already in deploy zone. -- Check if already in deploy zone.
local inzone=self:IsInZone(Zone) --Zone:IsCoordinateInZone(self:GetCoordinate()) local inzone=self:IsInZone(Zone) --Zone:IsCoordinateInZone(self:GetCoordinate())
local airbaseDeploy=nil --Wrapper.Airbase#AIRBASE -- Deploy airbase (if any).
if Zone and Zone:IsInstanceOf("ZONE_AIRBASE") then local airbaseDeploy=self.cargoTZC.DeployAirbase --Wrapper.Airbase#AIRBASE
airbaseDeploy=Zone:GetAirbase()
end
-- Check if group is already ready for loading. -- Check if group is already ready for loading.
local ready2deploy=false local ready2deploy=false
@ -6679,7 +6728,7 @@ function OPSGROUP:onafterTransport(From, Event, To)
if inzone then if inzone then
-- We are already in the pickup zone ==> wait and initiate unloading. -- We are already in the deploy zone ==> wait and initiate unloading.
if (self:IsArmygroup() or self:IsNavygroup()) and not self:IsHolding() then if (self:IsArmygroup() or self:IsNavygroup()) and not self:IsHolding() then
self:FullStop() self:FullStop()
end end
@ -6693,6 +6742,7 @@ function OPSGROUP:onafterTransport(From, Event, To)
local Coordinate=nil --Core.Point#COORDINATE local Coordinate=nil --Core.Point#COORDINATE
if self.cargoTransport.carrierGroup and self.cargoTransport.carrierGroup:IsLoading() then if self.cargoTransport.carrierGroup and self.cargoTransport.carrierGroup:IsLoading() then
--TODO: What the heck is self.cargoTransport.carrierGroup and where is this set?!
-- Coordinate of the new carrier. -- Coordinate of the new carrier.
Coordinate=self.cargoTransport.carrierGroup:GetCoordinate() Coordinate=self.cargoTransport.carrierGroup:GetCoordinate()
else else
@ -6701,14 +6751,14 @@ function OPSGROUP:onafterTransport(From, Event, To)
end end
-- Add waypoint. -- Add waypoint.
if self.isFlightgroup then if self:IsFlightgroup() then
if airbaseDeploy then
--- ---
-- Deploy at airbase -- Deploy at airbase
--- ---
if airbaseDeploy then
local airbaseCurrent=self.currbase local airbaseCurrent=self.currbase
if airbaseCurrent then if airbaseCurrent then
@ -6718,10 +6768,10 @@ function OPSGROUP:onafterTransport(From, Event, To)
self:StartUncontrolled() self:StartUncontrolled()
end end
else
-- Order group to land at an airbase.
self:LandAtAirbase(airbaseDeploy)
end end
-- Order group to land at an airbase.
self:__LandAtAirbase(-0.1, airbaseDeploy)
elseif self.isHelo then elseif self.isHelo then
@ -6735,28 +6785,26 @@ function OPSGROUP:onafterTransport(From, Event, To)
-- Cancel landedAt task. This should trigger Cruise once airborne. -- Cancel landedAt task. This should trigger Cruise once airborne.
if self:IsFlightgroup() and self:IsLandedAt() then if self:IsFlightgroup() and self:IsLandedAt() then
local Task=self:GetTaskCurrent() local Task=self:GetTaskCurrent()
--if Task and Task.description=="Task_Landed_At" then self:__TaskCancel(5, Task)
self:__TaskCancel(5, Task)
--end
end end
else else
self:E(self.lid.."ERROR: Carrier aircraft cannot land in Deploy zone! Specify a ZONE_AIRBASE as deploy zone") self:E(self.lid.."ERROR: Carrier aircraft cannot land in Deploy zone! Specify a ZONE_AIRBASE as deploy zone")
end end
elseif self.isArmygroup then elseif self:IsArmygroup() then
local cwp=self:GetWaypointCurrent() local cwp=self:GetWaypointCurrent()
local uid=cwp and cwp.uid or nil local uid=cwp and cwp.uid or nil
local path=self.cargoTransport:_GetPathTransport() -- Get transport path.
local path=self.cargoTransport:_GetPathTransport(self.cargoTZC)
if path then if path then
-- Loop over coordinates. -- Loop over coordinates.
for i,coordinate in pairs(path) do for i,coordinate in pairs(path) do
local waypoint=ARMYGROUP.AddWaypoint(self, coordinate, nil, uid) ; waypoint.temp=true local waypoint=ARMYGROUP.AddWaypoint(self, coordinate, nil, uid) ; waypoint.temp=true
uid=waypoint.uid uid=waypoint.uid
--coordinate:MarkToAll(string.format("Path i=%d, UID=%d", i, uid))
end end
end end
@ -6766,20 +6814,19 @@ function OPSGROUP:onafterTransport(From, Event, To)
-- Give cruise command. -- Give cruise command.
self:Cruise() self:Cruise()
elseif self.isNavygroup then elseif self:IsNavygroup() then
local cwp=self:GetWaypointCurrent() local cwp=self:GetWaypointCurrent()
local uid=cwp and cwp.uid or nil local uid=cwp and cwp.uid or nil
-- Get a (random) pre-defined transport path. -- Get a (random) pre-defined transport path.
local path=self.cargoTransport:_GetPathTransport() local path=self.cargoTransport:_GetPathTransport(self.cargoTZC)
if path then if path then
-- Loop over coordinates. -- Loop over coordinates.
for i,coordinate in pairs(path) do for i,coordinate in pairs(path) do
local waypoint=NAVYGROUP.AddWaypoint(self, coordinate, nil, uid) ; waypoint.temp=true local waypoint=NAVYGROUP.AddWaypoint(self, coordinate, nil, uid) ; waypoint.temp=true
uid=waypoint.uid uid=waypoint.uid
--coordinate:MarkToAll(string.format("Path i=%d, UID=%d", i, uid))
end end
end end
@ -6806,9 +6853,9 @@ function OPSGROUP:onafterUnloading(From, Event, To)
self:_NewCarrierStatus(OPSGROUP.CarrierStatus.UNLOADING) self:_NewCarrierStatus(OPSGROUP.CarrierStatus.UNLOADING)
-- Deploy zone. -- Deploy zone.
local zone=self.cargoTransport.disembarkzone or self.cargoTransport.deployzone --Core.Zone#ZONE local zone=self.cargoTZC.DisembarkZone or self.cargoTZC.DeployZone --Core.Zone#ZONE
for _,_cargo in pairs(self.cargoTransport.cargos) do for _,_cargo in pairs(self.cargoTZC.Cargos) do
local cargo=_cargo --#OPSGROUP.CargoGroup local cargo=_cargo --#OPSGROUP.CargoGroup
-- Check that cargo is loaded into this group. -- Check that cargo is loaded into this group.
@ -6821,11 +6868,11 @@ function OPSGROUP:onafterUnloading(From, Event, To)
local carrierGroup=nil local carrierGroup=nil
if self.cargoTransport.disembarkCarriers and #self.cargoTransport.disembarkCarriers>0 then if self.cargoTZC.DisembarkCarriers and #self.cargoTZC.DisembarkCarriers>0 then
needscarrier=true needscarrier=true
carrier, carrierGroup=self.cargoTransport:FindTransferCarrierForCargo(cargo.opsgroup, zone) carrier, carrierGroup=self.cargoTransport:FindTransferCarrierForCargo(cargo.opsgroup, zone, self.cargoTZC)
--TODO: max unloading time if transfer carrier does not arrive in the zone. --TODO: max unloading time if transfer carrier does not arrive in the zone.
@ -6863,7 +6910,7 @@ function OPSGROUP:onafterUnloading(From, Event, To)
-- Delivered to deploy zone -- Delivered to deploy zone
--- ---
if self.cargoTransport.disembarkInUtero then if self.cargoTransport:GetDisembarkInUtero(self.cargoTZC) then
-- Unload but keep "in utero" (no coordinate provided). -- Unload but keep "in utero" (no coordinate provided).
self:Unload(cargo.opsgroup) self:Unload(cargo.opsgroup)
@ -6872,10 +6919,10 @@ function OPSGROUP:onafterUnloading(From, Event, To)
local Coordinate=nil local Coordinate=nil
if self.cargoTransport.disembarkzone then if self.cargoTransport:GetDisembarkZone(self.cargoTZC) then
-- Random coordinate in disembark zone. -- Random coordinate in disembark zone.
Coordinate=self.cargoTransport.disembarkzone:GetRandomCoordinate() Coordinate=self.cargoTransport:GetDisembarkZone(self.cargoTZC):GetRandomCoordinate()
else else
@ -6891,7 +6938,7 @@ function OPSGROUP:onafterUnloading(From, Event, To)
local Heading=math.random(0,359) local Heading=math.random(0,359)
-- Unload to Coordinate. -- Unload to Coordinate.
self:Unload(cargo.opsgroup, Coordinate, self.cargoTransport.disembarkActivation, Heading) self:Unload(cargo.opsgroup, Coordinate, self.cargoTransport:GetDisembarkActivation(self.cargoTZC), Heading)
end end
@ -7050,7 +7097,7 @@ function OPSGROUP:onafterUnloadingDone(From, Event, To)
-- Cancel landedAt task. -- Cancel landedAt task.
if self:IsFlightgroup() and self:IsLandedAt() then if self:IsFlightgroup() and self:IsLandedAt() then
local Task=self:GetTaskCurrent() local Task=self:GetTaskCurrent()
self:TaskCancel(Task) self:__TaskCancel(5, Task)
end end
-- Check everything was delivered (or is dead). -- Check everything was delivered (or is dead).
@ -7058,16 +7105,22 @@ function OPSGROUP:onafterUnloadingDone(From, Event, To)
if not delivered then if not delivered then
local transportZone=self.cargoTransport:_GetPathPickup() self.cargoTZC=self.cargoTransport:_GetTransportZoneCombo(self)
if transportZone then if self.cargoTZC then
-- Pickup the next batch. -- Pickup the next batch.
self:I(self.lid.."Unloaded: Still cargo left ==> Pickup") self:I(self.lid.."Unloaded: Still cargo left ==> Pickup")
self:Pickup(transportZone) self:Pickup()
else else
env.info("FF error not implemented case!")
-- Debug info.
self:I(self.lid..string.format("WARNING: Not all cargo was delivered but could not get a transport zone combo ==> setting carrier state to NOT CARRIER"))
-- This is not a carrier anymore.
self:_NewCarrierStatus(OPSGROUP.CarrierStatus.NOTCARRIER)
end end
else else
@ -7128,6 +7181,7 @@ function OPSGROUP:onafterDelivered(From, Event, To, CargoTransport)
-- No current transport any more. -- No current transport any more.
self.cargoTransport=nil self.cargoTransport=nil
self.cargoTZC=nil
end end
-- Remove cargo transport from cargo queue. -- Remove cargo transport from cargo queue.
@ -7531,8 +7585,8 @@ end
-- @param #OPSGROUP self -- @param #OPSGROUP self
function OPSGROUP:_CheckStuck() function OPSGROUP:_CheckStuck()
-- Holding means we are not stuck. -- Cases we are not stuck.
if self:IsHolding() or self:Is("Rearming") then if self:IsHolding() or self:Is("Rearming") or self:IsWaiting() then
return return
end end
@ -7569,6 +7623,9 @@ function OPSGROUP:_CheckStuck()
-- Debug warning. -- Debug warning.
self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime)) self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime))
-- Give cruise command again.
self:__Cruise(1)
--TODO: Stuck event! --TODO: Stuck event!
@ -7916,13 +7973,16 @@ function OPSGROUP:_InitWaypoints(WpIndexMin, WpIndexMax)
-- Get home and destination airbases from waypoints. -- Get home and destination airbases from waypoints.
self.homebase=self.homebase or self:GetHomebaseFromWaypoints() self.homebase=self.homebase or self:GetHomebaseFromWaypoints()
self.destbase=self.destbase or self:GetDestinationFromWaypoints() local destbase=self:GetDestinationFromWaypoints()
self.destbase=self.destbase or destbase
self.currbase=self:GetHomebaseFromWaypoints() self.currbase=self:GetHomebaseFromWaypoints()
-- Remove the landing waypoint. We use RTB for that. It makes adding new waypoints easier as we do not have to check if the last waypoint is the landing waypoint. -- Remove the landing waypoint. We use RTB for that. It makes adding new waypoints easier as we do not have to check if the last waypoint is the landing waypoint.
if self.destbase and #self.waypoints>1 then if destbase and #self.waypoints>1 then
table.remove(self.waypoints, #self.waypoints) table.remove(self.waypoints, #self.waypoints)
else end
if self.destbase==nil then
self.destbase=self.homebase self.destbase=self.homebase
end end
@ -8051,7 +8111,7 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid)
-- Get next waypoint. Tricky part is that if -- Get next waypoint. Tricky part is that if
local wpnext=opsgroup:GetWaypointNext() local wpnext=opsgroup:GetWaypointNext()
if wpnext and (opsgroup.currentwp<#opsgroup.waypoints or opsgroup.adinfinitum or wpistemp) then if wpnext then --and (opsgroup.currentwp<#opsgroup.waypoints or opsgroup.adinfinitum or wpistemp)
-- Debug info. -- Debug info.
opsgroup:T(opsgroup.lid..string.format("Next waypoint UID=%d index=%d", wpnext.uid, opsgroup:GetWaypointIndex(wpnext.uid))) opsgroup:T(opsgroup.lid..string.format("Next waypoint UID=%d index=%d", wpnext.uid, opsgroup:GetWaypointIndex(wpnext.uid)))
@ -8074,6 +8134,12 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid)
opsgroup:_PassedFinalWaypoint(true, "_PassingWaypoint No next Waypoint found") opsgroup:_PassedFinalWaypoint(true, "_PassingWaypoint No next Waypoint found")
end end
-- Check if final waypoint was reached.
if opsgroup.currentwp==#opsgroup.waypoints and not opsgroup.adinfinitum then
-- Set passed final waypoint.
opsgroup:_PassedFinalWaypoint(true, "_PassingWaypoint currentwp==#waypoints and NOT adinfinitum")
end
-- Trigger PassingWaypoint event. -- Trigger PassingWaypoint event.
if waypoint.temp then if waypoint.temp then
@ -8122,9 +8188,13 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid)
if opsgroup:IsFlightgroup() then if opsgroup:IsFlightgroup() then
-- Land at current pos and wait for 60 min max. -- Land at current pos and wait for 60 min max.
env.info("FF LandAt for Pickup") local coordinate=nil
--opsgroup:LandAt(opsgroup:GetCoordinate(), 60*60) if opsgroup.cargoTZC then
opsgroup:LandAt(opsgroup.cargoTransport.pickupzone:GetCoordinate(), 60*60) coordinate=opsgroup.cargoTZC.PickupZone:GetCoordinate()
else
coordinate=opsgroup:GetCoordinate()
end
opsgroup:LandAt(coordinate, 60*60)
else else
@ -8136,11 +8206,16 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid)
elseif opsgroup:IsTransporting() then elseif opsgroup:IsTransporting() then
if opsgroup.isFlightgroup then if opsgroup:IsFlightgroup() then
-- Land at current pos and wait for 60 min max. -- Land at current pos and wait for 60 min max.
env.info("FF LandAt for Transporting") local coordinate=nil
opsgroup:LandAt(opsgroup:GetCoordinate(), 60*60) if opsgroup.cargoTZC then
coordinate=opsgroup.cargoTZC.DeployZone:GetCoordinate()
else
coordinate=opsgroup:GetCoordinate()
end
opsgroup:LandAt(coordinate, 60*60)
else else
-- Stop and unload. -- Stop and unload.

View File

@ -29,8 +29,7 @@
-- @field #string lid Log ID. -- @field #string lid Log ID.
-- @field #number uid Unique ID of the transport. -- @field #number uid Unique ID of the transport.
-- @field #number verbose Verbosity level. -- @field #number verbose Verbosity level.
-- @field #table cargos Cargos. Each element is a @{Ops.OpsGroup#OPSGROUP.CargoGroup}. --
-- @field #table carriers Carriers assigned for this transport.
-- @field #number prio Priority of this transport. Should be a number between 0 (high prio) and 100 (low prio). -- @field #number prio Priority of this transport. Should be a number between 0 (high prio) and 100 (low prio).
-- @field #boolean urgent If true, transport is urgent. -- @field #boolean urgent If true, transport is urgent.
-- @field #number importance Importance of this transport. Smaller=higher. -- @field #number importance Importance of this transport. Smaller=higher.
@ -38,22 +37,21 @@
-- @field #number Tstop Stop time in *abs.* seconds. Default `#nil` (never stops). -- @field #number Tstop Stop time in *abs.* seconds. Default `#nil` (never stops).
-- @field #number duration Duration (`Tstop-Tstart`) of the transport in seconds. -- @field #number duration Duration (`Tstop-Tstart`) of the transport in seconds.
-- @field #table conditionStart Start conditions. -- @field #table conditionStart Start conditions.
-- @field #table transportZones Table of transport zones. Each element of the table is of type `#OPSTRANSPORT.TransportZone`. --
-- @field Core.Zone#ZONE pickupzone Zone where the cargo is picked up. -- @field #table cargos Cargos. Each element is a @{Ops.OpsGroup#OPSGROUP.CargoGroup}.
-- @field Core.Zone#ZONE deployzone Zone where the cargo is dropped off. -- @field #table carriers Carriers assigned for this transport.
-- @field Core.Zone#ZONE embarkzone (Optional) Zone where the cargo is supposed to embark. Default is the pickup zone. --
-- @field Core.Zone#ZONE disembarkzone (Optional) Zone where the cargo is disembarked. Default is the deploy zone. -- @field #table tzCombos Table of transport zone combos. Each element of the table is of type `#OPSTRANSPORT.TransportZoneCombo`.
-- @field #boolean disembarkActivation Activation setting when group is disembared from carrier. -- @field #number tzcCounter Running number of added transport zone combos.
-- @field #boolean disembarkInUtero Do not spawn the group in any any state but leave it "*in utero*". For example, to directly load it into another carrier. -- @field #OPSTRANSPORT.TransportZoneCombo tzcDefault Default transport zone combo.
-- @field #table disembarkCarriers Table of carriers to which the cargo is disembared. This is a direct transfer from the old to the new carrier. --
-- @field #table requiredCargos Table of cargo groups that must be loaded before the first transport is started.
-- @field #number Ncargo Total number of cargo groups. -- @field #number Ncargo Total number of cargo groups.
-- @field #number Ncarrier Total number of assigned carriers. -- @field #number Ncarrier Total number of assigned carriers.
-- @field #number Ndelivered Total number of cargo groups delivered. -- @field #number Ndelivered Total number of cargo groups delivered.
-- @field #table pathsTransport Transport paths of `#OPSGROUP.Path`. --
-- @field #table pathsPickup Pickup paths of `#OPSGROUP.Path`.
-- @field Ops.Auftrag#AUFTRAG mission The mission attached to this transport. -- @field Ops.Auftrag#AUFTRAG mission The mission attached to this transport.
-- @field #table assets Warehouse assets assigned for this transport. -- @field #table assets Warehouse assets assigned for this transport.
--
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- *Victory is the beautiful, bright-colored flower. Transport is the stem without which it could never have blossomed.* -- Winston Churchill --- *Victory is the beautiful, bright-colored flower. Transport is the stem without which it could never have blossomed.* -- Winston Churchill
@ -113,15 +111,13 @@
-- @field #OPSTRANSPORT -- @field #OPSTRANSPORT
OPSTRANSPORT = { OPSTRANSPORT = {
ClassName = "OPSTRANSPORT", ClassName = "OPSTRANSPORT",
verbose = 0, verbose = 0,
cargos = {}, cargos = {},
carriers = {}, carriers = {},
carrierTransportStatus = {}, carrierTransportStatus = {},
transportZones = {}, tzCombos = {},
tzcCounter = 0,
conditionStart = {}, conditionStart = {},
pathsTransport = {},
pathsPickup = {},
requiredCargos = {},
assets = {}, assets = {},
} }
@ -143,13 +139,23 @@ OPSTRANSPORT.Status={
} }
--- Pickup and deploy set. --- Pickup and deploy set.
-- @type OPSTRANSPORT.TransportZone -- @type OPSTRANSPORT.TransportZoneCombo
-- @field #number uid Unique ID of the TZ combo.
-- @field #number Ncarriers Number of carrier groups using this transport zone.
-- @field #number Ncargo Number of cargos assigned. This is a running number and *not* decreased if cargo is delivered or dead.
-- @field #table Cargos Cargo groups of the TZ combo. Each element is of type `Ops.OpsGroup#OPSGROUP.CargoGroup`.
-- @field Core.Zone#ZONE PickupZone Pickup zone. -- @field Core.Zone#ZONE PickupZone Pickup zone.
-- @field Core.Zone#ZONE DeployZone Deploy zone. -- @field Core.Zone#ZONE DeployZone Deploy zone.
-- @field Core.Zone#ZONE EmbarkZone Embark zone if different from pickup zone. -- @field Core.Zone#ZONE EmbarkZone Embark zone if different from pickup zone.
-- @field #OPSTRANSPORT.Path PickupPath Path for pickup. -- @field Core.Zone#ZONE DisembarkZone Zone where the troops are disembared to.
-- @field #OPSTRANSPORT.Path TransportPath Path for Transport. -- @field Wrapper.Airbase#AIRBASE PickupAirbase Airbase for pickup.
-- @field #numberr Ncarriers Number of carrier groups using this transport zone. -- @field Wrapper.Airbase#AIRBASE DeployAirbase Airbase for deploy.
-- @field #table PickupPaths Paths for pickup.
-- @field #table TransportPaths Path for Transport. Each elment of the table is of type `#OPSTRANSPORT.Path`.
-- @field #table RequiredCargos Required cargos.
-- @field #table DisembarkCarriers Carriers where the cargo is directly disembarked to.
-- @field #boolean disembarkActivation If true, troops are spawned in late activated state when disembarked from carrier.
-- @field #boolean disembarkInUtero If true, troops are disembarked "in utero".
--- Path used for pickup or transport. --- Path used for pickup or transport.
-- @type OPSTRANSPORT.Path -- @type OPSTRANSPORT.Path
@ -167,7 +173,7 @@ _OPSTRANSPORTID=0
--- Army Group version. --- Army Group version.
-- @field #string version -- @field #string version
OPSTRANSPORT.version="0.3.0" OPSTRANSPORT.version="0.4.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -184,11 +190,11 @@ OPSTRANSPORT.version="0.3.0"
--- Create a new OPSTRANSPORT class object. Essential input are the troops that should be transported and the zones where the troops are picked up and deployed. --- Create a new OPSTRANSPORT class object. Essential input are the troops that should be transported and the zones where the troops are picked up and deployed.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Core.Set#SET_GROUP GroupSet Set of groups to be transported. Can also be a single @{Wrapper.Group#GROUP} or @{Ops.OpsGroup#OPSGROUP} object. -- @param Core.Set#SET_GROUP CargoGroups Groups to be transported as cargo. Can also be a single @{Wrapper.Group#GROUP} or @{Ops.OpsGroup#OPSGROUP} object.
-- @param Core.Zone#ZONE Pickupzone Pickup zone. This is the zone, where the carrier is going to pickup the cargo. **Important**: only cargo is considered, if it is in this zone when the carrier starts loading! -- @param Core.Zone#ZONE PickupZone Pickup zone. This is the zone, where the carrier is going to pickup the cargo. **Important**: only cargo is considered, if it is in this zone when the carrier starts loading!
-- @param Core.Zone#ZONE Deployzone Deploy zone. This is the zone, where the carrier is going to drop off the cargo. -- @param Core.Zone#ZONE DeployZone Deploy zone. This is the zone, where the carrier is going to drop off the cargo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:New(GroupSet, Pickupzone, Deployzone) function OPSTRANSPORT:New(CargoGroups, PickupZone, DeployZone)
-- Inherit everything from FSM class. -- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #OPSTRANSPORT local self=BASE:Inherit(self, FSM:New()) -- #OPSTRANSPORT
@ -201,26 +207,21 @@ function OPSTRANSPORT:New(GroupSet, Pickupzone, Deployzone)
-- UID of this transport. -- UID of this transport.
self.uid=_OPSTRANSPORTID self.uid=_OPSTRANSPORTID
-- Defaults. -- Defaults.
self:SetPickupZone(Pickupzone)
self:SetDeployZone(Deployzone)
self:SetEmbarkZone() -- Default is pickup zone.
self.cargos={} self.cargos={}
self.carriers={} self.carriers={}
self.Ncargo=0 self.Ncargo=0
self.Ncarrier=0 self.Ncarrier=0
self.Ndelivered=0 self.Ndelivered=0
self:SetPriority() self:SetPriority()
self:SetTime() self:SetTime()
-- Add cargo groups (could also be added later).
if GroupSet then
self:AddCargoGroups(GroupSet)
end
-- Set default TZC.
self.tzcDefault=self:AddTransportZoneCombo(PickupZone, DeployZone, CargoGroups)
-- FMS start state is PLANNED. -- FMS start state is PLANNED.
self:SetStartState(OPSTRANSPORT.Status.PLANNED) self:SetStartState(OPSTRANSPORT.Status.PLANNED)
@ -255,11 +256,54 @@ end
-- User Functions -- User Functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add pickup and deploy zone combination.
-- @param #OPSTRANSPORT self
-- @param Core.Zone#ZONE PickupZone Zone where the troops are picked up.
-- @param Core.Zone#ZONE DeployZone Zone where the troops are picked up.
-- @param Core.Set#SET_GROUP CargoGroups Groups to be transported as cargo. Can also be a single @{Wrapper.Group#GROUP} or @{Ops.OpsGroup#OPSGROUP} object.
-- @return #OPSTRANSPORT.TransportZoneCombo Transport zone table.
function OPSTRANSPORT:AddTransportZoneCombo(PickupZone, DeployZone, CargoGroups)
-- Increase counter.
self.tzcCounter=self.tzcCounter+1
local tzcombo={} --#OPSTRANSPORT.TransportZoneCombo
-- Init.
tzcombo.uid=self.tzcCounter
tzcombo.Ncarriers=0
tzcombo.Ncargo=0
tzcombo.Cargos={}
tzcombo.RequiredCargos={}
tzcombo.DisembarkCarriers={}
tzcombo.PickupPaths={}
tzcombo.TransportPaths={}
-- Set zones.
self:SetPickupZone(PickupZone, tzcombo)
self:SetDeployZone(DeployZone, tzcombo)
self:SetEmbarkZone(nil, tzcombo)
-- Add cargo groups (could also be added later).
if CargoGroups then
self:AddCargoGroups(CargoGroups, tzcombo)
end
-- Add to table.
table.insert(self.tzCombos, tzcombo)
return tzcombo
end
--- Add cargo groups to be transported. --- Add cargo groups to be transported.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Core.Set#SET_GROUP GroupSet Set of groups to be transported. Can also be passed as a single GROUP or OPSGROUP object. -- @param Core.Set#SET_GROUP GroupSet Set of groups to be transported. Can also be passed as a single GROUP or OPSGROUP object.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:AddCargoGroups(GroupSet) function OPSTRANSPORT:AddCargoGroups(GroupSet, TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-- Check type of GroupSet provided. -- Check type of GroupSet provided.
if GroupSet:IsInstanceOf("GROUP") or GroupSet:IsInstanceOf("OPSGROUP") then if GroupSet:IsInstanceOf("GROUP") or GroupSet:IsInstanceOf("OPSGROUP") then
@ -268,8 +312,13 @@ function OPSTRANSPORT:AddCargoGroups(GroupSet)
local cargo=self:_CreateCargoGroupData(GroupSet) local cargo=self:_CreateCargoGroupData(GroupSet)
if cargo then if cargo then
-- Add to main table.
table.insert(self.cargos, cargo) table.insert(self.cargos, cargo)
self.Ncargo=self.Ncargo+1 self.Ncargo=self.Ncargo+1
-- Add to TZC table.
table.insert(TransportZoneCombo.Cargos, cargo)
TransportZoneCombo.Ncargo=TransportZoneCombo.Ncargo+1
end end
else else
@ -281,8 +330,13 @@ function OPSTRANSPORT:AddCargoGroups(GroupSet)
local cargo=self:_CreateCargoGroupData(group) local cargo=self:_CreateCargoGroupData(group)
if cargo then if cargo then
-- Add to main table.
table.insert(self.cargos, cargo) table.insert(self.cargos, cargo)
self.Ncargo=self.Ncargo+1 self.Ncargo=self.Ncargo+1
-- Add to TZC table.
table.insert(TransportZoneCombo.Cargos, cargo)
TransportZoneCombo.Ncargo=TransportZoneCombo.Ncargo+1
end end
end end
@ -306,97 +360,174 @@ function OPSTRANSPORT:AddCargoGroups(GroupSet)
return self return self
end end
--- Add pickup and deploy zone combination. Optionally, embark and disembark zones can be specified.
--
-- * The pickup zone is a zone where
-- * bla
--
-- @param #OPSTRANSPORT self
-- @param Core.Zone#ZONE PickupZone Zone where the troops are picked up.
-- @return #OPSTRANSPORT.TransportZone Transport zone table.
function OPSTRANSPORT:AddTransportZones(PickupZone, DeployZone, EmbarkZone, DisembarkZone, PickupPath, TransportPath)
local transport={} --#OPSTRANSPORT.TransportZone
transport.PickupZone=PickupZone
transport.DeployZone=DeployZone
transport.EmbarkZone=EmbarkZone or PickupZone
transport.DisembarkZone=DisembarkZone
transport.PickupPath=PickupPath
transport.TransportPath=TransportPath
transport.Ncarriers=0
table.insert(self.transportZones, transport)
return transport
end
--- Set pickup zone. --- Set pickup zone.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Core.Zone#ZONE PickupZone Zone where the troops are picked up. -- @param Core.Zone#ZONE PickupZone Zone where the troops are picked up.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:SetPickupZone(PickupZone) function OPSTRANSPORT:SetPickupZone(PickupZone, TransportZoneCombo)
self.pickupzone=PickupZone
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
TransportZoneCombo.PickupZone=PickupZone
if PickupZone and PickupZone:IsInstanceOf("ZONE_AIRBASE") then
TransportZoneCombo.PickupAirbase=PickupZone._.ZoneAirbase
end
return self return self
end end
--- Get pickup zone.
-- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return Core.Zone#ZONE Zone where the troops are picked up.
function OPSTRANSPORT:GetPickupZone(TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
return TransportZoneCombo.PickupZone
end
--- Set deploy zone. --- Set deploy zone.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Core.Zone#ZONE DeployZone Zone where the troops are deployed. -- @param Core.Zone#ZONE DeployZone Zone where the troops are deployed.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:SetDeployZone(DeployZone) function OPSTRANSPORT:SetDeployZone(DeployZone, TransportZoneCombo)
self.deployzone=DeployZone
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-- Set deploy zone.
TransportZoneCombo.DeployZone=DeployZone
-- Check if this is an airbase.
if DeployZone and DeployZone:IsInstanceOf("ZONE_AIRBASE") then
TransportZoneCombo.DeployAirbase=DeployZone._.ZoneAirbase
end
return self return self
end end
--- Get deploy zone.
-- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return Core.Zone#ZONE Zone where the troops are deployed.
function OPSTRANSPORT:GetDeployZone(TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
return TransportZoneCombo.DeployZone
end
--- Set embark zone. --- Set embark zone.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Core.Zone#ZONE EmbarkZone Zone where the troops are embarked. -- @param Core.Zone#ZONE EmbarkZone Zone where the troops are embarked.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:SetEmbarkZone(EmbarkZone) function OPSTRANSPORT:SetEmbarkZone(EmbarkZone, TransportZoneCombo)
self.embarkzone=EmbarkZone or self.pickupzone
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
TransportZoneCombo.EmbarkZone=EmbarkZone or TransportZoneCombo.PickupZone
return self return self
end end
--- Get embark zone.
-- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return Core.Zone#ZONE Zone where the troops are embarked from.
function OPSTRANSPORT:GetEmbarkZone(TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
return TransportZoneCombo.EmbarkZone
end
--- Set disembark zone. --- Set disembark zone.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Core.Zone#ZONE DisembarkZone Zone where the troops are disembarked. -- @param Core.Zone#ZONE DisembarkZone Zone where the troops are disembarked.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:SetDisembarkZone(DisembarkZone) function OPSTRANSPORT:SetDisembarkZone(DisembarkZone, TransportZoneCombo)
self.disembarkzone=DisembarkZone
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
TransportZoneCombo.DisembarkZone=DisembarkZone
return self return self
end end
--- Get disembark zone.
-- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return Core.Zone#ZONE Zone where the troops are disembarked to.
function OPSTRANSPORT:GetDisembarkZone(TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
return TransportZoneCombo.DisembarkZone
end
--- Set activation status of group when disembarked from transport carrier. --- Set activation status of group when disembarked from transport carrier.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param #boolean Active If `true` or `nil`, group is activated when disembarked. If `false`, group is late activated and needs to be activated manually. -- @param #boolean Active If `true` or `nil`, group is activated when disembarked. If `false`, group is late activated and needs to be activated manually.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:SetDisembarkActivation(Active) function OPSTRANSPORT:SetDisembarkActivation(Active, TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
if Active==true or Active==nil then if Active==true or Active==nil then
self.disembarkActivation=true TransportZoneCombo.disembarkActivation=true
else else
self.disembarkActivation=false TransportZoneCombo.disembarkActivation=false
end end
return self return self
end end
--- Get disembark activation.
-- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #boolean If `true`, groups are spawned in late activated state.
function OPSTRANSPORT:GetDisembarkActivation(TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
return TransportZoneCombo.disembarkActivation
end
--- Set transfer carrier(s). These are carrier groups, where the cargo is directly loaded into when disembarked. --- Set transfer carrier(s). These are carrier groups, where the cargo is directly loaded into when disembarked.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Core.Set#SET_GROUP Carriers Carrier set. Can also be passed as a #GROUP, #OPSGROUP or #SET_OPSGROUP object. -- @param Core.Set#SET_GROUP Carriers Carrier set. Can also be passed as a #GROUP, #OPSGROUP or #SET_OPSGROUP object.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:SetDisembarkCarriers(Carriers) function OPSTRANSPORT:SetDisembarkCarriers(Carriers, TransportZoneCombo)
-- Debug info. -- Debug info.
self:T(self.lid.."Setting transfer carriers!") self:T(self.lid.."Setting transfer carriers!")
-- Create table. -- Use default TZC if no transport zone combo is provided.
self.disembarkCarriers=self.disembarkCarriers or {} TransportZoneCombo=TransportZoneCombo or self.tzcDefault
if Carriers:IsInstanceOf("GROUP") or Carriers:IsInstanceOf("OPSGROUP") then if Carriers:IsInstanceOf("GROUP") or Carriers:IsInstanceOf("OPSGROUP") then
local carrier=self:_GetOpsGroupFromObject(Carriers) local carrier=self:_GetOpsGroupFromObject(Carriers)
if carrier then if carrier then
table.insert(self.disembarkCarriers, carrier) table.insert(TransportZoneCombo.DisembarkCarriers, carrier)
end end
elseif Carriers:IsInstanceOf("SET_GROUP") or Carriers:IsInstanceOf("SET_OPSGROUP") then elseif Carriers:IsInstanceOf("SET_GROUP") or Carriers:IsInstanceOf("SET_OPSGROUP") then
@ -404,7 +535,7 @@ function OPSTRANSPORT:SetDisembarkCarriers(Carriers)
for _,object in pairs(Carriers:GetSet()) do for _,object in pairs(Carriers:GetSet()) do
local carrier=self:_GetOpsGroupFromObject(object) local carrier=self:_GetOpsGroupFromObject(object)
if carrier then if carrier then
table.insert(self.disembarkCarriers, carrier) table.insert(TransportZoneCombo.DisembarkCarriers, carrier)
end end
end end
@ -415,38 +546,72 @@ function OPSTRANSPORT:SetDisembarkCarriers(Carriers)
return self return self
end end
--- Get transfer carrier(s). These are carrier groups, where the cargo is directly loaded into when disembarked.
-- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #table Table of carriers.
function OPSTRANSPORT:GetDisembarkCarriers(TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
return TransportZoneCombo.DisembarkCarriers
end
--- Set if group remains *in utero* after disembarkment from carrier. Can be used to directly load the group into another carrier. Similar to disembark in late activated state. --- Set if group remains *in utero* after disembarkment from carrier. Can be used to directly load the group into another carrier. Similar to disembark in late activated state.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param #boolean InUtero If `true` or `nil`, group remains *in utero* after disembarkment. -- @param #boolean InUtero If `true` or `nil`, group remains *in utero* after disembarkment.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:SetDisembarkInUtero(InUtero) function OPSTRANSPORT:SetDisembarkInUtero(InUtero, TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
if InUtero==true or InUtero==nil then if InUtero==true or InUtero==nil then
self.disembarkInUtero=true TransportZoneCombo.disembarkInUtero=true
else else
self.disembarkInUtero=false TransportZoneCombo.disembarkInUtero=false
end end
return self return self
end end
--- Get disembark in utero.
-- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #boolean If `true`, groups stay in utero after disembarkment.
function OPSTRANSPORT:GetDisembarkInUtero(TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
return TransportZoneCombo.disembarkInUtero
end
--- Set required cargo. This is a list of cargo groups that need to be loaded before the **first** transport will start. --- Set required cargo. This is a list of cargo groups that need to be loaded before the **first** transport will start.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Core.Set#SET_GROUP Cargos Required cargo set. Can also be passed as a #GROUP, #OPSGROUP or #SET_OPSGROUP object. -- @param Core.Set#SET_GROUP Cargos Required cargo set. Can also be passed as a #GROUP, #OPSGROUP or #SET_OPSGROUP object.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:SetRequiredCargos(Cargos) function OPSTRANSPORT:SetRequiredCargos(Cargos, TransportZoneCombo)
-- Debug info. -- Debug info.
self:T(self.lid.."Setting required cargos!") self:T(self.lid.."Setting required cargos!")
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-- Create table. -- Create table.
self.requiredCargos=self.requiredCargos or {} TransportZoneCombo.RequiredCargos=TransportZoneCombo.RequiredCargos or {}
if Cargos:IsInstanceOf("GROUP") or Cargos:IsInstanceOf("OPSGROUP") then if Cargos:IsInstanceOf("GROUP") or Cargos:IsInstanceOf("OPSGROUP") then
local cargo=self:_GetOpsGroupFromObject(Cargos) local cargo=self:_GetOpsGroupFromObject(Cargos)
if cargo then if cargo then
table.insert(self.requiredCargos, cargo) table.insert(TransportZoneCombo.RequiredCargos, cargo)
end end
elseif Cargos:IsInstanceOf("SET_GROUP") or Cargos:IsInstanceOf("SET_OPSGROUP") then elseif Cargos:IsInstanceOf("SET_GROUP") or Cargos:IsInstanceOf("SET_OPSGROUP") then
@ -454,7 +619,7 @@ function OPSTRANSPORT:SetRequiredCargos(Cargos)
for _,object in pairs(Cargos:GetSet()) do for _,object in pairs(Cargos:GetSet()) do
local cargo=self:_GetOpsGroupFromObject(object) local cargo=self:_GetOpsGroupFromObject(object)
if cargo then if cargo then
table.insert(self.requiredCargos, cargo) table.insert(TransportZoneCombo.RequiredCargos, cargo)
end end
end end
@ -465,6 +630,17 @@ function OPSTRANSPORT:SetRequiredCargos(Cargos)
return self return self
end end
--- Get required cargos. This is a list of cargo groups that need to be loaded before the **first** transport will start.
-- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #table Table of required cargo ops groups.
function OPSTRANSPORT:GetRequiredCargos(TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
return TransportZoneCombo.RequiredCargos
end
--- Add a carrier assigned for this transport. --- Add a carrier assigned for this transport.
@ -538,11 +714,14 @@ end
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param #boolean Delivered If `true`, only delivered groups are returned. If `false` only undelivered groups are returned. If `nil`, all groups are returned. -- @param #boolean Delivered If `true`, only delivered groups are returned. If `false` only undelivered groups are returned. If `nil`, all groups are returned.
-- @param Ops.OpsGroup#OPSGROUP Carrier (Optional) Only count cargo groups that fit into the given carrier group. Current cargo is not a factor. -- @param Ops.OpsGroup#OPSGROUP Carrier (Optional) Only count cargo groups that fit into the given carrier group. Current cargo is not a factor.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #table Cargo Ops groups. Can be and empty table `{}`. -- @return #table Cargo Ops groups. Can be and empty table `{}`.
function OPSTRANSPORT:GetCargoOpsGroups(Delivered, Carrier) function OPSTRANSPORT:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo)
local cargos=self:GetCargos(TransportZoneCombo)
local opsgroups={} local opsgroups={}
for _,_cargo in pairs(self.cargos) do for _,_cargo in pairs(cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if Delivered==nil or cargo.delivered==Delivered then if Delivered==nil or cargo.delivered==Delivered then
if cargo.opsgroup and not (cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped()) then if cargo.opsgroup and not (cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped()) then
@ -556,13 +735,26 @@ function OPSTRANSPORT:GetCargoOpsGroups(Delivered, Carrier)
return opsgroups return opsgroups
end end
--- Get carrier @{Ops.OpsGroup#OPSGROUP}s. --- Get carriers.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @return #table Carrier Ops groups. -- @return #table Carrier Ops groups.
function OPSTRANSPORT:GetCarrierOpsGroups() function OPSTRANSPORT:GetCarriers()
return self.carriers return self.carriers
end end
--- Get cargos.
-- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #table Cargos.
function OPSTRANSPORT:GetCargos(TransportZoneCombo)
if TransportZoneCombo then
return TransportZoneCombo.Cargos
else
return self.cargos
end
end
--- Set transport start and stop time. --- Set transport start and stop time.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
@ -652,8 +844,12 @@ end
-- @param #boolean Reversed If `true`, add waypoints of group in reversed order. -- @param #boolean Reversed If `true`, add waypoints of group in reversed order.
-- @param #number Radius Randomization radius in meters. Default 0 m. -- @param #number Radius Randomization radius in meters. Default 0 m.
-- @param #number Altitude Altitude in feet AGL. Only for aircraft. -- @param #number Altitude Altitude in feet AGL. Only for aircraft.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport Zone combo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:AddPathTransport(PathGroup, Reversed, Radius, Altitude) function OPSTRANSPORT:AddPathTransport(PathGroup, Reversed, Radius, Altitude, TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
local path={} --#OPSTRANSPORT.Path local path={} --#OPSTRANSPORT.Path
path.coords={} path.coords={}
@ -679,20 +875,26 @@ function OPSTRANSPORT:AddPathTransport(PathGroup, Reversed, Radius, Altitude)
-- Add path. -- Add path.
table.insert(self.pathsTransport, path) table.insert(TransportZoneCombo.TransportPaths, path)
return self return self
end end
--- Get a path for transportation. --- Get a path for transportation.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport Zone combo.
-- @return #table The path of COORDINATEs. -- @return #table The path of COORDINATEs.
function OPSTRANSPORT:_GetPathTransport() function OPSTRANSPORT:_GetPathTransport(TransportZoneCombo)
if self.pathsTransport and #self.pathsTransport>0 then -- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
local pathsTransport=TransportZoneCombo.TransportPaths
if pathsTransport and #pathsTransport>0 then
-- Get a random path for transport. -- Get a random path for transport.
local path=self.pathsTransport[math.random(#self.pathsTransport)] --#OPSTRANSPORT.Path local path=pathsTransport[math.random(#pathsTransport)] --#OPSTRANSPORT.Path
local coordinates={} local coordinates={}
@ -718,8 +920,12 @@ end
-- @param #boolean Reversed If `true`, add waypoints of group in reversed order. -- @param #boolean Reversed If `true`, add waypoints of group in reversed order.
-- @param #number Radius Randomization radius in meters. Default 0 m. -- @param #number Radius Randomization radius in meters. Default 0 m.
-- @param #number Altitude Altitude in feet AGL. Only for aircraft. -- @param #number Altitude Altitude in feet AGL. Only for aircraft.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport Zone combo.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:AddPathPickup(PathGroup, Reversed, Radius, Altitude) function OPSTRANSPORT:AddPathPickup(PathGroup, Reversed, Radius, Altitude, TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
local path={} --#OPSTRANSPORT.Path local path={} --#OPSTRANSPORT.Path
path.coords={} path.coords={}
@ -744,20 +950,26 @@ function OPSTRANSPORT:AddPathPickup(PathGroup, Reversed, Radius, Altitude)
end end
-- Add path. -- Add path.
table.insert(self.pathsPickup, path) table.insert(TransportZoneCombo.PickupPaths, path)
return self return self
end end
--- Get a path for pickup. --- Get a path for pickup.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport Zone combo.
-- @return #table The path of COORDINATEs. -- @return #table The path of COORDINATEs.
function OPSTRANSPORT:_GetPathPickup() function OPSTRANSPORT:_GetPathPickup(TransportZoneCombo)
if self.pathsPickup and #self.pathsPickup>0 then -- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
local paths=TransportZoneCombo.PickupPaths
if paths and #paths>0 then
-- Get a random path for transport. -- Get a random path for transport.
local path=self.pathsPickup[math.random(#self.pathsPickup)] --#OPSTRANSPORT.Path local path=paths[math.random(#paths)] --#OPSTRANSPORT.Path
local coordinates={} local coordinates={}
@ -857,8 +1069,8 @@ function OPSTRANSPORT:IsReadyToGo()
-- Pickup AND deploy zones must be set. -- Pickup AND deploy zones must be set.
local gotzones=false local gotzones=false
for _,_tz in pairs(self.transportZones) do for _,_tz in pairs(self.tzCombos) do
local tz=_tz --#OPSTRANSPORT.TransportZone local tz=_tz --#OPSTRANSPORT.TransportZoneCombo
if tz.PickupZone and tz.DeployZone then if tz.PickupZone and tz.DeployZone then
gotzones=true gotzones=true
break break
@ -961,8 +1173,8 @@ function OPSTRANSPORT:onafterStatus(From, Event, To)
-- Info about cargo and carrier. -- Info about cargo and carrier.
if self.verbose>=2 then if self.verbose>=2 then
for i,_tz in pairs(self.transportZones) do for i,_tz in pairs(self.tzCombos) do
local tz=_tz --#OPSTRANSPORT.TransportZone local tz=_tz --#OPSTRANSPORT.TransportZoneCombo
text=text..string.format("\n[%d] %s --> %s", i, tz.PickupZone and tz.PickupZone:GetName() or "Unknown", tz.DeployZone and tz.DeployZone and tz.DeployZone:GetName() or "Unknown", tz.Ncarriers) text=text..string.format("\n[%d] %s --> %s", i, tz.PickupZone and tz.PickupZone:GetName() or "Unknown", tz.DeployZone and tz.DeployZone and tz.DeployZone:GetName() or "Unknown", tz.Ncarriers)
end end
@ -1166,17 +1378,23 @@ end
--- Check if all required cargos are loaded. --- Check if all required cargos are loaded.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #boolean If true, all required cargos are loaded or there is no required cargo. -- @return #boolean If true, all required cargos are loaded or there is no required cargo.
function OPSTRANSPORT:_CheckRequiredCargos() function OPSTRANSPORT:_CheckRequiredCargos(TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
if self.requiredCargos==nil or #self.requiredCargos==0 then local requiredCargos=TransportZoneCombo.RequiredCargos
if requiredCargos==nil or #requiredCargos==0 then
return true return true
end end
local carrierNames=self:_GetCarrierNames() local carrierNames=self:_GetCarrierNames()
local gotit=true local gotit=true
for _,_cargo in pairs(self.requiredCargos) do for _,_cargo in pairs(requiredCargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP local cargo=_cargo --Ops.OpsGroup#OPSGROUP
@ -1219,26 +1437,30 @@ end
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Ops.OpsGroup#OPSGROUP CargoGroup The cargo group that needs to be loaded into a carrier unit/element of the carrier group. -- @param Ops.OpsGroup#OPSGROUP CargoGroup The cargo group that needs to be loaded into a carrier unit/element of the carrier group.
-- @param Core.Zone#ZONE Zone (Optional) Zone where the carrier must be in. -- @param Core.Zone#ZONE Zone (Optional) Zone where the carrier must be in.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return Ops.OpsGroup#OPSGROUP.Element New carrier element for cargo or nil. -- @return Ops.OpsGroup#OPSGROUP.Element New carrier element for cargo or nil.
-- @return Ops.OpsGroup#OPSGROUP New carrier group for cargo or nil. -- @return Ops.OpsGroup#OPSGROUP New carrier group for cargo or nil.
function OPSTRANSPORT:FindTransferCarrierForCargo(CargoGroup, Zone) function OPSTRANSPORT:FindTransferCarrierForCargo(CargoGroup, Zone, TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
local carrier=nil --Ops.OpsGroup#OPSGROUP.Element local carrier=nil --Ops.OpsGroup#OPSGROUP.Element
local carrierGroup=nil --Ops.OpsGroup#OPSGROUP local carrierGroup=nil --Ops.OpsGroup#OPSGROUP
--TODO: maybe sort the carriers wrt to largest free cargo bay. Or better smallest free cargo bay that can take the cargo group weight. --TODO: maybe sort the carriers wrt to largest free cargo bay. Or better smallest free cargo bay that can take the cargo group weight.
for _,_carrier in pairs(self.disembarkCarriers or {}) do for _,_carrier in pairs(TransportZoneCombo.DisembarkCarriers) do
local carrierGroup=_carrier --Ops.OpsGroup#OPSGROUP local carrierGroup=_carrier --Ops.OpsGroup#OPSGROUP
-- First check if carrier is alive and loading cargo. -- First check if carrier is alive and loading cargo.
if carrierGroup and carrierGroup:IsAlive() and (carrierGroup:IsLoading() or self.deployzone:IsInstanceOf("ZONE_AIRBASE")) then if carrierGroup and carrierGroup:IsAlive() and (carrierGroup:IsLoading() or TransportZoneCombo.DeployAirbase) then
-- Find an element of the group that has enough free space. -- Find an element of the group that has enough free space.
carrier=carrierGroup:FindCarrierForCargo(CargoGroup) carrier=carrierGroup:FindCarrierForCargo(CargoGroup)
if carrier then if carrier then
if Zone==nil or Zone:IsCoordinateInZone(carrier.unit:GetCoordinate()) then if Zone==nil or Zone:IsVec2InZone(carrier.unit:GetVec2()) then
return carrier, carrierGroup return carrier, carrierGroup
else else
self:T2(self.lid.."Got transfer carrier but carrier not in zone (yet)!") self:T2(self.lid.."Got transfer carrier but carrier not in zone (yet)!")
@ -1277,10 +1499,11 @@ end
-- @param Core.Zone#ZONE Zone The zone object. -- @param Core.Zone#ZONE Zone The zone object.
-- @param #boolean Delivered If `true`, only delivered groups are returned. If `false` only undelivered groups are returned. If `nil`, all groups are returned. -- @param #boolean Delivered If `true`, only delivered groups are returned. If `false` only undelivered groups are returned. If `nil`, all groups are returned.
-- @param Ops.OpsGroup#OPSGROUP Carrier (Optional) Only count cargo groups that fit into the given carrier group. Current cargo is not a factor. -- @param Ops.OpsGroup#OPSGROUP Carrier (Optional) Only count cargo groups that fit into the given carrier group. Current cargo is not a factor.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #number Number of cargo groups. -- @return #number Number of cargo groups.
function OPSTRANSPORT:_CountCargosInZone(Zone, Delivered, Carrier) function OPSTRANSPORT:_CountCargosInZone(Zone, Delivered, Carrier, TransportZoneCombo)
local cargos=self:GetCargoOpsGroups(Delivered, Carrier) local cargos=self:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo)
local N=0 local N=0
for _,_cargo in pairs(cargos) do for _,_cargo in pairs(cargos) do
@ -1293,37 +1516,52 @@ function OPSTRANSPORT:_CountCargosInZone(Zone, Delivered, Carrier)
return N return N
end end
--- Get a pickup zone for a carrier group. This will be a zone, where the most cargo groups are located that fit into the carrier. --- Get a transport zone combination (TZC) for a carrier group. The pickup zone will be a zone, where the most cargo groups are located that fit into the carrier.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Ops.OpsGroup#OPSGROUP Carrier The carrier OPS group. -- @param Ops.OpsGroup#OPSGROUP Carrier The carrier OPS group.
-- @return Core.Zone#ZONE Pickup zone or `#nil`. -- @return Core.Zone#ZONE Pickup zone or `#nil`.
function OPSTRANSPORT:_GetPickupZone(Carrier) function OPSTRANSPORT:_GetTransportZoneCombo(Carrier)
env.info(string.format("FF GetPickupZone")) --- Selection criteria
local pickup=nil -- * Distance: pickup zone should be as close as possible.
-- * Ncargo: Number of cargo groups. Pickup, where there is most cargo.
-- * Ncarrier: Number of carriers already "working" on this TZC. Would be better if not all carriers work on the same combo while others are ignored.
-- Get carrier position.
local vec2=Carrier:GetVec2()
local pickup=nil --#OPSTRANSPORT.TransportZoneCombo
local distmin=nil local distmin=nil
for i,_transportzone in pairs(self.transportZones) do for i,_transportzone in pairs(self.tzCombos) do
local tz=_transportzone --#OPSTRANSPORT.TransportZone local tz=_transportzone --#OPSTRANSPORT.TransportZoneCombo
-- Count cargos in pickup zone. -- Check that pickup and deploy zones were defined.
local ncargo=self:_CountCargosInZone(tz.PickupZone, false, Carrier) if tz.PickupZone and tz.DeployZone and tz.EmbarkZone then
env.info(string.format("FF GetPickupZone i=%d, ncargo=%d", i, ncargo)) -- Count undelivered cargos in embark(!) zone that fit into the carrier.
local ncargo=self:_CountCargosInZone(tz.EmbarkZone, false, Carrier, tz)
if ncargo>0 then
local vec2=Carrier:GetVec2()
local dist=tz.PickupZone:Get2DDistance(vec2) --env.info(string.format("FF GetPickupZone i=%d, ncargo=%d", i, ncargo))
if distmin==nil or dist<distmin then if ncargo>0 then
distmin=dist
pickup=tz local dist=tz.PickupZone:Get2DDistance(vec2)
if distmin==nil or dist<distmin then
distmin=dist
pickup=tz
end
end end
end end
end end
-- Debug info.
if pickup then
self:T(self.lid..string.format("Found pickupzone %s for carrier group %s", pickup.PickupZone:GetName(), Carrier:GetName()))
else
self:T(self.lid..string.format("Could NOT find a pickup zone (with cargo) for carrier group %s", Carrier:GetName()))
end
return pickup return pickup
end end
@ -1361,4 +1599,3 @@ function OPSTRANSPORT:_GetOpsGroupFromObject(Object)
return opsgroup return opsgroup
end end