From f84e2ede6602b7b1a630fb2fe0cfc926ab30720c Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 21 Aug 2023 01:02:04 +0200 Subject: [PATCH] OPSTRANSPORT Storage --- Moose Development/Moose/Ops/OpsGroup.lua | 124 +++++++++++++----- Moose Development/Moose/Ops/OpsTransport.lua | 131 ++++++++++++++++++- 2 files changed, 218 insertions(+), 37 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 6afe0d51e..da6ea0bba 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -493,7 +493,9 @@ OPSGROUP.CargoStatus={ --- Cargo group data. -- @type OPSGROUP.CargoGroup +-- @field #string type Type of cargo: "OPSGROUP" or "STORAGE". -- @field #OPSGROUP opsgroup The cargo opsgroup. +-- @field Ops.OpsTransport#OPSTRANSPORT.Storage storage Storage data. -- @field #boolean delivered If `true`, group was delivered. -- @field #boolean disembarkActivation If `true`, group is activated. If `false`, group is late activated. -- @field Core.Zone#ZONE disembarkZone Zone where this group is disembarked to. @@ -7993,13 +7995,17 @@ function OPSGROUP:_CheckCargoTransport() 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:GetCargos()) do local cargo=_cargo --#OPSGROUP.CargoGroup - local state=cargo.opsgroup:GetState() - local status=cargo.opsgroup.cargoStatus - local name=cargo.opsgroup.groupname - 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 (%d) %s [%s]: %s, carrier=%s(%s), delivered=%s", j, name, state, status, carrierGroupname, carrierElementname, tostring(cargo.delivered)) + if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then + local state=cargo.opsgroup:GetState() + local status=cargo.opsgroup.cargoStatus + local name=cargo.opsgroup.groupname + 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 (%d) %s [%s]: %s, carrier=%s(%s), delivered=%s", j, name, state, status, carrierGroupname, carrierElementname, tostring(cargo.delivered)) + else + --TODO: STORAGE + end end end if text~="" then @@ -8748,46 +8754,72 @@ end --- Check if the group can *in principle* be carrier of a cargo group. This checks the max cargo capacity of the group but *not* how much cargo is already loaded (if any). -- **Note** that the cargo group *cannot* be split into units, i.e. the largest cargo bay of any element of the group must be able to load the whole cargo group in one piece. -- @param #OPSGROUP self --- @param #OPSGROUP CargoGroup Cargo group, which needs a carrier. +-- @param Ops.OpsGroup#OPSGROUP.CargoGroup CargoGroup Cargo group, which needs a carrier. -- @return #boolean If `true`, there is an element of the group that can load the whole cargo group. -function OPSGROUP:CanCargo(CargoGroup) +function OPSGROUP:CanCargo(Cargo) - if CargoGroup then + if Cargo then + + local weight=math.huge + if Cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then - local weight=CargoGroup:GetWeightTotal() + local weight=Cargo.opsgroup:GetWeightTotal() - for _,_element in pairs(self.elements) do - local element=_element --#OPSGROUP.Element - - -- Check that element is not dead and has - if element and element.status~=OPSGROUP.ElementStatus.DEAD and element.weightMaxCargo>=weight then - return true + for _,_element in pairs(self.elements) do + local element=_element --#OPSGROUP.Element + + -- Check that element is not dead and has + if element and element.status~=OPSGROUP.ElementStatus.DEAD and element.weightMaxCargo>=weight then + return true + end end + + else + + if type(Cargo.storage.cargoType)=="number" then + weight=Cargo.storage.cargoAmount + else + weight=Cargo.storage.cargoAmount*100 + end + + local bay=0 + for _,_element in pairs(self.elements) do + local element=_element --#OPSGROUP.Element + + -- Check that element is not dead and has + if element and element.status~=OPSGROUP.ElementStatus.DEAD then + bay=bay+element.weightMaxCargo + end + end + + if bay>=weight then + return true + end + end + end return false end ---- Add weight to the internal cargo of an element of the group. +--- Find carrier for cargo by evaluating the free cargo bay storage. -- @param #OPSGROUP self --- @param #OPSGROUP CargoGroup Cargo group, which needs a carrier. +-- @param #number Weight Weight of cargo in kg. -- @return #OPSGROUP.Element Carrier able to transport the cargo. -function OPSGROUP:FindCarrierForCargo(CargoGroup) - - local weight=CargoGroup:GetWeightTotal() +function OPSGROUP:FindCarrierForCargo(Weight) for _,_element in pairs(self.elements) do local element=_element --#OPSGROUP.Element local free=self:GetFreeCargobay(element.name) - if free>=weight then + if free>=Weight then return element else - self:T3(self.lid..string.format("%s: Weight %d>%d free cargo bay", element.name, weight, free)) + self:T3(self.lid..string.format("%s: Weight %d>%d free cargo bay", element.name, Weight, free)) end end @@ -9097,14 +9129,14 @@ function OPSGROUP:onafterLoading(From, Event, To) local isNotCargo=cargo.opsgroup:IsNotCargo(true) -- Check if cargo is holding or loaded - local isHolding=cargo.opsgroup:IsHolding() or cargo.opsgroup:IsLoaded() + local isHolding=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup:IsHolding() or cargo.opsgroup:IsLoaded()) or true -- Check if cargo is in embark/pickup zone. -- Added InUtero here, if embark zone is moving (ship) and cargo has been spawned late activated and its position is not updated. Not sure if that breaks something else! - local inZone=cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone) or cargo.opsgroup:IsInUtero() + local inZone=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone) or cargo.opsgroup:IsInUtero()) or true -- Check if cargo is currently on a mission. - local isOnMission=cargo.opsgroup:IsOnMission() + local isOnMission=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsOnMission() or false -- Check if current mission is using this ops transport. if isOnMission then @@ -9114,12 +9146,29 @@ function OPSGROUP:onafterLoading(From, Event, To) end end + local isAvail=true + if cargo.type==OPSTRANSPORT.CargoType.STORAGE then + local nAvail=0 + if type(cargo.storage.cargoType)=="number" then + nAvail=cargo.storage.storageFrom:GetLiquidAmount(cargo.storage.cargoType) + else + nAvail=cargo.storage.storageFrom:GetItemAmount(cargo.storage.cargoType) + end + if nAvail>0 then + isAvail=true + else + isAvail=false + end + end + + local isDead=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsDead() or false + -- Debug message. self:T(self.lid..string.format("Loading: canCargo=%s, isCarrier=%s, isNotCargo=%s, isHolding=%s, isOnMission=%s", - tostring(canCargo), tostring(isCarrier), tostring(isNotCargo), tostring(isHolding), tostring(isOnMission))) + tostring(canCargo), tostring(isCarrier), tostring(isNotCargo), tostring(isHolding), tostring(isOnMission))) -- TODO: Need a better :IsBusy() function or :IsReadyForMission() :IsReadyForBoarding() :IsReadyForTransport() - if canCargo and inZone and isNotCargo and isHolding and (not (cargo.delivered or cargo.opsgroup:IsDead() or isCarrier or isOnMission)) then + if canCargo and inZone and isNotCargo and isHolding and isAvail and (not (cargo.delivered or isDead or isCarrier or isOnMission)) then table.insert(cargos, cargo) end end @@ -9135,9 +9184,24 @@ function OPSGROUP:onafterLoading(From, Event, To) -- Loop over all cargos. for _,_cargo in pairs(cargos) do local cargo=_cargo --#OPSGROUP.CargoGroup + + local weight=nil + if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then + + weight=cargo.opsgroup:GetWeightTotal() + + else + + if type(cargo.storage.cargoType)=="number" then + weight=cargo.storage.cargoAmount + else + weight=cargo.storage.cargoAmount*100 -- Assume 100 kg per item + end + + end -- Find a carrier for this cargo. - local carrier=self:FindCarrierForCargo(cargo.opsgroup) + local carrier=self:FindCarrierForCargo(weight) if carrier then diff --git a/Moose Development/Moose/Ops/OpsTransport.lua b/Moose Development/Moose/Ops/OpsTransport.lua index 56613fbd0..06870dfcd 100644 --- a/Moose Development/Moose/Ops/OpsTransport.lua +++ b/Moose Development/Moose/Ops/OpsTransport.lua @@ -186,6 +186,22 @@ OPSTRANSPORT.Status={ -- @field #number radius Radomization radius for waypoints in meters. Default 0 m. -- @field #boolean reverse If `true`, path is used in reversed order. +--- Storage data. +-- @type OPSTRANSPORT.Storage +-- @field Wrapper.Storage#STORAGE storageFrom Storage from. +-- @field Wrapper.Storage#STORAGE storageTo Storage To. +-- @field #string cargoType Type of cargo. +-- @field #number cargoAmount Amount of cargo. Liquids in kg. + +--- Storage data. +-- @type OPSTRANSPORT.CargoType +-- @field #string OPSGROUP Cargo is an OPSGROUP. +-- @field #string STORAGE Cargo is storage of DCS warehouse. +OPSTRANSPORT.CargoType={ + OPSGROUP="OPSGROUP", + STORAGE="STORAGE", +} + --- Generic transport condition. -- @type OPSTRANSPORT.Condition -- @field #function func Callback function to check for a condition. Should return a #boolean. @@ -576,6 +592,27 @@ function OPSTRANSPORT:AddCargoGroups(GroupSet, TransportZoneCombo, DisembarkActi return self end +--- Add cargo groups to be transported. +-- @param #OPSTRANSPORT self +-- @param Wrapper.Storage#STORAGE StorageFrom Storage from. +-- @param Wrapper.Storage#STORAGE StorageTo Storage to. +-- @param #string CargoType Type of cargo. +-- @param #number CargoAmount Amount of cargo. Liquids in kg. +-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. +-- @return #OPSTRANSPORT self +function OPSTRANSPORT:AddCargoStorage(StorageFrom, StorageTo, CargoType, CargoAmount, TransportZoneCombo) + + -- Use default TZC if no transport zone combo is provided. + TransportZoneCombo=TransportZoneCombo or self.tzcDefault + + -- Cargo data. + local cargo=self:_CreateCargoStorage(StorageFrom,StorageTo, CargoType, CargoAmount, TransportZoneCombo) + + -- Add to TZC table. + table.insert(TransportZoneCombo.Cargos, cargo) + +end + --- Set pickup zone. -- @param #OPSTRANSPORT self @@ -1068,7 +1105,7 @@ function OPSTRANSPORT:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo) local opsgroups={} for _,_cargo in pairs(cargos) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup - if Delivered==nil or cargo.delivered==Delivered then + if cargo.type=="OPSGROUP" and (Delivered==nil or cargo.delivered==Delivered) then if cargo.opsgroup and not (cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped()) then if Carrier==nil or Carrier:CanCargo(cargo.opsgroup) then table.insert(opsgroups, cargo.opsgroup) @@ -1080,6 +1117,29 @@ function OPSTRANSPORT:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo) return opsgroups end +--- Get (all) cargo @{Ops.OpsGroup#OPSGROUP}s. Optionally, only delivered or undelivered groups can be returned. +-- @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 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 `{}`. +function OPSTRANSPORT:GetCargoStorages(Delivered, Carrier, TransportZoneCombo) + + local cargos=self:GetCargos(TransportZoneCombo) + + local opsgroups={} + for _,_cargo in pairs(cargos) do + local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup + if cargo.type=="STORAGE" and (Delivered==nil or cargo.delivered==Delivered) then + if Carrier==nil or Carrier:CanCargo(cargo.opsgroup) then + table.insert(opsgroups, cargo.opsgroup) + end + end + end + + return opsgroups +end + --- Get carriers. -- @param #OPSTRANSPORT self -- @return #table Carrier Ops groups. @@ -1108,6 +1168,28 @@ function OPSTRANSPORT:GetCargos(TransportZoneCombo) end +--- Get total weight. +-- @param #OPSTRANSPORT self +-- @param Ops.OpsGroup#OPSGROUP.CargoGroup Cargo Cargo data. +-- @param #boolean IncludeReserved Include reserved cargo. +-- @return #number Weight in kg. +function OPSTRANSPORT:GetCargoTotalWeight(Cargo, IncludeReserved) + + local weight=0 + + if Cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then + weight=Cargo.opsgroup:GetWeightTotal(nil, IncludeReserved) + else + if type(Cargo.storage.cargoType)=="number" then + return Cargo.storage.cargoAmount + else + return Cargo.storage.cargoAmount*100 -- Assume 100 kg per item + end + end + + return weight +end + --- Set transport start and stop time. -- @param #OPSTRANSPORT self -- @param #string ClockStart Time the transport is started, e.g. "05:00" for 5 am. If specified as a #number, it will be relative (in seconds) to the current mission time. Default is 5 seconds after mission was added. @@ -1642,11 +1724,15 @@ function OPSTRANSPORT:onafterStatusUpdate(From, Event, To) text=text..string.format("\nCargos:") for _,_cargo in pairs(self:GetCargos()) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup - local carrier=cargo.opsgroup:_GetMyCarrierElement() - local name=carrier and carrier.name or "none" - local cstate=carrier and carrier.status or "N/A" - text=text..string.format("\n- %s: %s [%s], weight=%d kg, carrier=%s [%s], delivered=%s [UID=%s]", - cargo.opsgroup:GetName(), cargo.opsgroup.cargoStatus:upper(), cargo.opsgroup:GetState(), cargo.opsgroup:GetWeightTotal(), name, cstate, tostring(cargo.delivered), tostring(cargo.opsgroup.cargoTransportUID)) + if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then + local carrier=cargo.opsgroup:_GetMyCarrierElement() + local name=carrier and carrier.name or "none" + local cstate=carrier and carrier.status or "N/A" + text=text..string.format("\n- %s: %s [%s], weight=%d kg, carrier=%s [%s], delivered=%s [UID=%s]", + cargo.opsgroup:GetName(), cargo.opsgroup.cargoStatus:upper(), cargo.opsgroup:GetState(), cargo.opsgroup:GetWeightTotal(), name, cstate, tostring(cargo.delivered), tostring(cargo.opsgroup.cargoTransportUID)) + else + --TODO: Storage + end end text=text..string.format("\nCarriers:") @@ -2119,7 +2205,7 @@ function OPSTRANSPORT:_CreateCargoGroupData(group, TransportZoneCombo, Disembark -- Create a new data item. local cargo={} --Ops.OpsGroup#OPSGROUP.CargoGroup - + cargo.type="OPSGROUP" cargo.opsgroup=opsgroup cargo.delivered=false cargo.status="Unknown" @@ -2133,6 +2219,37 @@ function OPSTRANSPORT:_CreateCargoGroupData(group, TransportZoneCombo, Disembark return cargo end +--- Create a cargo group data structure. +-- @param #OPSTRANSPORT self +-- @param Wrapper.Storage#STORAGE StorageFrom Storage from. +-- @param Wrapper.Storage#STORAGE StorageTo Storage to. +-- @param #string CargoType Type of cargo. +-- @param #number CargoAmount Amount of cargo. Liquids in kg. +-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. +-- @return Ops.OpsGroup#OPSGROUP.CargoGroup Cargo group data. +function OPSTRANSPORT:_CreateCargoStorage(StorageFrom, StorageTo, CargoType, CargoAmount, TransportZoneCombo) + + local storage={} --#OPSTRANSPORT.Storage + storage.storageFrom=StorageFrom + storage.storageTo=StorageTo + storage.cargoType=CargoType + storage.cargoAmount=CargoAmount + + + -- Create a new data item. + local cargo={} --Ops.OpsGroup#OPSGROUP.CargoGroup + cargo.type="STORAGE" + cargo.opsgroup=nil + cargo.storage=storage + cargo.delivered=false + cargo.status="Unknown" + cargo.tzcUID=TransportZoneCombo + cargo.disembarkZone=nil + cargo.disembarkCarriers=nil + + return cargo +end + --- Count how many cargo groups are inside a zone. -- @param #OPSTRANSPORT self -- @param Core.Zone#ZONE Zone The zone object.