From 887f7aa3e9820a03be8f84b10bda53b11b316c9a Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 23 Aug 2023 23:37:23 +0200 Subject: [PATCH] OPSTRANSPORT - Storage --- Moose Development/Moose/Ops/OpsGroup.lua | 351 ++++++++++++++++--- Moose Development/Moose/Ops/OpsTransport.lua | 44 ++- 2 files changed, 331 insertions(+), 64 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index b9cc6884f..84c2c9c75 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -489,10 +489,14 @@ OPSGROUP.CargoStatus={ --- Element cargo bay data. -- @type OPSGROUP.MyCargo -- @field #OPSGROUP group The cargo group. +-- @field #number storageType Type of storage. +-- @field #number storageAmount Amount of storage. +-- @field #number storageWeight Weight of storage item. -- @field #boolean reserved If `true`, the cargo bay space is reserved but cargo has not actually been loaded yet. --- Cargo group data. -- @type OPSGROUP.CargoGroup +-- @field #number uid Unique ID of this cargo data. -- @field #string type Type of cargo: "OPSGROUP" or "STORAGE". -- @field #OPSGROUP opsgroup The cargo opsgroup. -- @field Ops.OpsTransport#OPSTRANSPORT.Storage storage Storage data. @@ -7974,7 +7978,11 @@ function OPSGROUP:_CheckCargoTransport() local element=_element --#OPSGROUP.Element for _,_cargo in pairs(element.cargoBay) do local cargo=_cargo --#OPSGROUP.MyCargo - text=text..string.format("\n- %s in carrier %s, reserved=%s", tostring(cargo.group:GetName()), tostring(element.name), tostring(cargo.reserved)) + if cargo.group then + text=text..string.format("\n- %s in carrier %s, reserved=%s", tostring(cargo.group:GetName()), tostring(element.name), tostring(cargo.reserved)) + else + text=text..string.format("\n- storage %s=%d kg in carrier %s", tostring(cargo.storageType), tostring(cargo.storageAmount*cargo.storageWeight), tostring(element.name)) + end end end if text=="" then @@ -8242,6 +8250,8 @@ function OPSGROUP:_AddCargobay(CargoGroup, CarrierElement, Reserved) if cargo then cargo.reserved=Reserved else + + --cargo=self:_CreateMyCargo(CargoUID, CargoGroup) cargo={} --#OPSGROUP.MyCargo cargo.group=CargoGroup @@ -8270,6 +8280,95 @@ function OPSGROUP:_AddCargobay(CargoGroup, CarrierElement, Reserved) return self end +--- Add warehouse storage to cargo bay of a carrier. +-- @param #OPSGROUP self +-- @param #OPSGROUP.Element CarrierElement The element of the carrier. +-- @param #number CargoUID UID of the cargo data. +-- @param #string StorageType Storage type. +-- @param #number StorageAmount Storage amount. +-- @param #number StorageWeight Weight of a single storage item in kg. +function OPSGROUP:_AddCargobayStorage(CarrierElement, CargoUID, StorageType, StorageAmount, StorageWeight) + + local MyCargo=self:_CreateMyCargo(CargoUID, nil, StorageType, StorageAmount, StorageWeight) + + self:_AddMyCargoBay(MyCargo, CarrierElement) + + +end + +--- Add OPSGROUP to cargo bay of a carrier. +-- @param #OPSGROUP self +-- @param #number CargoUID UID of the cargo data. +-- @param #OPSGROUP OpsGroup Cargo group. +-- @param #string StorageType Storage type. +-- @param #number StorageAmount Storage amount. +-- @param #number StorageWeight Weight of a single storage item in kg. +-- @return #OPSGROUP.MyCargo My cargo object. +function OPSGROUP:_CreateMyCargo(CargoUID, OpsGroup, StorageType, StorageAmount, StorageWeight) + + local cargo={} --#OPSGROUP.MyCargo + + cargo.cargoUID=CargoUID + cargo.group=OpsGroup + cargo.storageType=StorageType + cargo.storageAmount=StorageAmount + cargo.storageWeight=StorageWeight + cargo.reserved=false + + return cargo +end + + +--- Add storage to cargo bay of a carrier. +-- @param #OPSGROUP self +-- @param #OPSGROUP.MyCargo MyCargo My cargo. +-- @param #OPSGROUP.Element CarrierElement The element of the carrier. +function OPSGROUP:_AddMyCargoBay(MyCargo, CarrierElement) + + table.insert(CarrierElement.cargoBay, MyCargo) + + if not MyCargo.reserved then + + -- Cargo weight. + local weight=0 + + if MyCargo.group then + weight=MyCargo.group:GetWeightTotal() + else + weight=MyCargo.storageAmount*MyCargo.storageWeight + end + + -- Add weight to carrier. + self:AddWeightCargo(CarrierElement.name, weight) + + end + + +end + +--- Get cargo bay data from a cargo data id. +-- @param #OPSGROUP self +-- @param #number uid Unique ID of cargo data. +-- @return #OPSGROUP.MyCargo Cargo My cargo. +-- @return #OPSGROUP.Element Element that has loaded the cargo. +function OPSGROUP:_GetMyCargoBayFromUID(uid) + + for _,_element in pairs(self.elements) do + local element=_element --#OPSGROUP.Element + + for i,_mycargo in pairs(element.cargoBay) do + local mycargo=_mycargo --#OPSGROUP.MyCargo + + if mycargo.cargoUID and mycargo.cargoUID==uid then + return mycargo, element, i + end + end + end + + return nil, nil, nil +end + + --- Get all groups currently loaded as cargo. -- @param #OPSGROUP self -- @param #string CarrierName (Optional) Only return cargo groups loaded into a particular carrier unit. @@ -8317,6 +8416,49 @@ function OPSGROUP:_GetCargobay(CargoGroup) return nil, nil, nil end +--- Remove OPSGROUP from cargo bay of a carrier. +-- @param #OPSGROUP self +-- @param #OPSGROUP.Element Element Cargo group. +-- @param #number CargoUID Cargo UID. +-- @return #OPSGROUP.MyCargo MyCargo My cargo data. +function OPSGROUP:_GetCargobayElement(Element, CargoUID) + + for i,_mycargo in pairs(Element.cargoBay) do + local mycargo=_mycargo --#OPSGROUP.MyCargo + + if mycargo.cargoUID and mycargo.cargoUID==CargoUID then + return mycargo + end + + end + +end + +--- Remove OPSGROUP from cargo bay of a carrier. +-- @param #OPSGROUP self +-- @param #OPSGROUP.Element Element Cargo group. +-- @param #OPSGROUP.MyCargo MyCargo My cargo data. +-- @return #boolean If `true`, cargo could be removed. +function OPSGROUP:_DelCargobayElement(Element, MyCargo) + + for i,_mycargo in pairs(Element.cargoBay) do + local mycargo=_mycargo --#OPSGROUP.MyCargo + + if mycargo.cargoUID and MyCargo.cargoUID and mycargo.cargoUID==MyCargo.cargoUID then + if MyCargo.group then + self:RedWeightCargo(Element.name, MyCargo.group:GetWeightTotal()) + else + self:RedWeightCargo(Element.name, MyCargo.storageAmount*MyCargo.storageWeight) + end + table.remove(Element.cargoBay, i) + return true + end + + end + + return false +end + --- Remove OPSGROUP from cargo bay of a carrier. -- @param #OPSGROUP self -- @param #OPSGROUP CargoGroup Cargo group. @@ -8680,8 +8822,11 @@ function OPSGROUP:GetWeightCargo(UnitName, IncludeReserved) for _,_cargo in pairs(element.cargoBay) do local cargo=_cargo --#OPSGROUP.MyCargo if (not cargo.reserved) or (cargo.reserved==true and (IncludeReserved==true or IncludeReserved==nil)) then - local cargoweight=cargo.group:GetWeightTotal() - gewicht=gewicht+cargoweight + if cargo.group then + gewicht=gewicht+cargo.group:GetWeightTotal() + else + gewicht=gewicht+cargo.storageAmount*cargo.storageWeight + end --self:I(self.lid..string.format("unit=%s (reserved=%s): cargo=%s weight=%d, total weight=%d", tostring(UnitName), tostring(IncludeReserved), cargo.group:GetName(), cargoweight, weight)) end end @@ -8775,6 +8920,32 @@ function OPSGROUP:RedWeightCargo(UnitName, Weight) return self end +--- Get weight of warehouse storage to transport. +-- @param #OPSGROUP self +-- @param Ops.OpsTransport#OPSTRANSPORT.Storage Storage +-- @param #boolean Total Get total weight. Otherweise the already delivered or lost weight is substracted. +-- @param #boolean Reserved Reduce weight that is reserved. +-- @param #boolean Amount Return amount not weight. +-- @return #number Weight of cargo in kg or amount in number of items, if `Amount=true`. +function OPSGROUP:_GetWeightStorage(Storage, Total, Reserved, Amount) + + local weight=Storage.cargoAmount + + if not Total then + weight=weight-Storage.cargoLost-Storage.cargoLoaded-Storage.cargoDelivered + end + + if Reserved then + weight=weight-Storage.cargoReserved + end + + if not Amount then + weight=weight*Storage.cargoWeight + end + + return weight +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 @@ -8785,6 +8956,7 @@ function OPSGROUP:CanCargo(Cargo) if Cargo then local weight=math.huge + if Cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then local weight=Cargo.opsgroup:GetWeightTotal() @@ -8801,28 +8973,32 @@ function OPSGROUP:CanCargo(Cargo) 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 + --- + -- STORAGE + --- + -- Since storage cargo can be devided onto multiple carriers, we take the weight of a single cargo item (even 1 kg of fuel). + weight=Cargo.storage.cargoWeight + end + -- Calculate cargo bay space. + 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 + + -- Check if cargo fits into cargo bay(s) of carrier group. + if bay>=weight then + return true + end + end @@ -9217,37 +9393,70 @@ function OPSGROUP:onafterLoading(From, Event, To) if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then weight=cargo.opsgroup:GetWeightTotal() + + -- Find a carrier for this cargo. + local carrier=self:FindCarrierForCargo(weight) + + -- Order cargo group to board the carrier. + if carrier then + cargo.opsgroup:Board(self, carrier) + end 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 + --- + -- STORAGE + --- - -- Find a carrier for this cargo. - local carrier=self:FindCarrierForCargo(weight) - - if carrier then + -- Get weight of cargo that needs to be transported. + weight=self:_GetWeightStorage(cargo.storage, false) - -- Order cargo group to board the carrier. - if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then - cargo.opsgroup:Board(self, carrier) - else - env.info("FF loading storage!") + -- Debug info. + env.info(string.format("FF loading storage weight=%d kg!", weight)) + + -- Loop over all elements of the carrier group. + for _,_element in pairs(self.elements) do + local element=_element --#OPSGROUP.Element + + -- Get the free cargo space of the carrier. + local free=self:GetFreeCargobay(element.name) + + -- Min of weight or bay. + local w=math.min(weight, free) + + -- Calculate item amount. + local amount=w/cargo.storage.cargoWeight + + -- Remove items from warehouse. if type(cargo.storage.cargoType)=="number" then - cargo.storage.storageFrom:RemoveLiquid(cargo.storage.cargoType, cargo.storage.cargoAmount) + cargo.storage.storageFrom:RemoveLiquid(cargo.storage.cargoType, amount) else - cargo.storage.storageFrom:RemoveItem(cargo.storage.cargoType, cargo.storage.cargoAmount) + cargo.storage.storageFrom:RemoveItem(cargo.storage.cargoType, amount) end + + -- Add amount to loaded cargo. + cargo.storage.cargoLoaded=cargo.storage.cargoLoaded+amount + + -- Add cargo to cargo by of element. + self:_AddCargobayStorage(element, cargo.uid, cargo.storage.cargoType, amount, cargo.storage.cargoWeight) + + -- Reduce weight for the next element (if any). + weight=weight-w + + -- Debug info. + local text=string.format("FF element %s: amount=%d, weight=%d, left=%d", element.name, amount, w, weight) + env.info(text) + + -- If no cargo left, break the loop. + if weight<=0 then + break + end + end - - end - + + end end + end --- Set (new) cargo status. @@ -9726,18 +9935,52 @@ function OPSGROUP:onafterUnloading(From, Event, To) -- STORAGE --- - if type(cargo.storage.cargoType)=="number" then - cargo.storage.storageTo:AddLiquid(cargo.storage.cargoType, cargo.storage.cargoAmount) - --cargo.storage.amountDelivered=cargo.storage.amountDelivered+ - else - cargo.storage.storageTo:AddItem(cargo.storage.cargoType, cargo.storage.cargoAmount) + for _,_element in pairs(self.elements) do + local element=_element --#OPSGROUP.Element + + -- Get my cargo from cargo bay of element. + local mycargo=self:_GetCargobayElement(element, cargo.uid) + + if mycargo then + + -- Add cargo to warehouse storage. + if type(mycargo.storageType)=="number" then + cargo.storage.storageTo:AddLiquid(mycargo.storageType, mycargo.storageAmount) + else + cargo.storage.storageTo:AddItem(cargo.storage.cargoType, cargo.storage.cargoAmount) + end + + -- Add amount to delivered. + cargo.storage.cargoDelivered=cargo.storage.cargoDelivered+mycargo.storageAmount + + -- Reduce loaded amount. + cargo.storage.cargoLoaded=cargo.storage.cargoLoaded-mycargo.storageAmount + + -- Remove cargo from bay. + self:_DelCargobayElement(element, mycargo) + + end end - -- Cargo was delivered (somehow). - cargo.delivered=true - - -- Increase number of delivered cargos. - self.cargoTransport.Ndelivered=self.cargoTransport.Ndelivered+1 + -- Get amount that was delivered. + local amountDelivered=self:_GetWeightStorage(cargo.storage, false, false, true) + + -- Get total amount to be delivered. + local amountTotal=self:_GetWeightStorage(cargo.storage, true, false, true) + + -- Debug info. + local text=string.format("FF Amount delivered=%d, total=%d", amountDelivered, amountTotal) + self:I(self.lid..text) + + if amountDelivered>=amountTotal then + + -- Cargo was delivered (somehow). + cargo.delivered=true + + -- Increase number of delivered cargos. + self.cargoTransport.Ndelivered=self.cargoTransport.Ndelivered+1 + + end end @@ -10171,7 +10414,7 @@ function OPSGROUP:onafterBoard(From, Event, To, CarrierGroup, Carrier) -- Debug info. self:T(self.lid..string.format("Group is loaded currently ==> Moving directly to new carrier - No Unload(), Disembart() events triggered!")) - -- Remove my carrier. + -- Remove old/current carrier. self:_RemoveMyCarrier() -- Trigger Load event. diff --git a/Moose Development/Moose/Ops/OpsTransport.lua b/Moose Development/Moose/Ops/OpsTransport.lua index 570666853..394b134c4 100644 --- a/Moose Development/Moose/Ops/OpsTransport.lua +++ b/Moose Development/Moose/Ops/OpsTransport.lua @@ -63,6 +63,7 @@ -- @field Ops.Chief#CHIEF chief Chief of the transport. -- @field Ops.OpsZone#OPSZONE opszone OPS zone. -- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the transport was cancelled before the request is processed. +-- @field #number cargocounter Running number to generate cargo UIDs. -- -- @extends Core.Fsm#FSM @@ -131,6 +132,7 @@ OPSTRANSPORT = { legions = {}, statusLegion = {}, requestID = {}, + cargocounter = 0, } --- Cargo transport status. @@ -191,7 +193,12 @@ OPSTRANSPORT.Status={ -- @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. +-- @field #number cargoAmount Amount of cargo that should be transported. +-- @field #number cargoReserved Amount of cargo that is reserved for a carrier group. +-- @field #number cargoDelivered Amount of cargo that has been delivered. +-- @field #number cargoLost Amount of cargo that was lost. +-- @field #number cargoLoaded Amount of cargo that is loading. +-- @field #number cargoWeight Weight of one single cargo item in kg. Default 1 kg. --- Storage data. -- @type OPSTRANSPORT.CargoType @@ -212,7 +219,7 @@ _OPSTRANSPORTID=0 --- Army Group version. -- @field #string version -OPSTRANSPORT.version="0.7.0" +OPSTRANSPORT.version="0.8.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -599,15 +606,16 @@ end -- @param Wrapper.Storage#STORAGE StorageTo Storage to. -- @param #string CargoType Type of cargo. -- @param #number CargoAmount Amount of cargo. Liquids in kg. +-- @param #number CargoWeight Weight of a single cargo item in kg. Default 1 kg. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. -- @return #OPSTRANSPORT self -function OPSTRANSPORT:AddCargoStorage(StorageFrom, StorageTo, CargoType, CargoAmount, TransportZoneCombo) +function OPSTRANSPORT:AddCargoStorage(StorageFrom, StorageTo, CargoType, CargoAmount, CargoWeight, 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) + local cargo=self:_CreateCargoStorage(StorageFrom,StorageTo, CargoType, CargoAmount, CargoWeight, TransportZoneCombo) -- Add to TZC table. table.insert(TransportZoneCombo.Cargos, cargo) @@ -1186,9 +1194,17 @@ function OPSTRANSPORT:GetCargoTotalWeight(Cargo, IncludeReserved) weight=Cargo.opsgroup:GetWeightTotal(nil, IncludeReserved) else if type(Cargo.storage.cargoType)=="number" then - return Cargo.storage.cargoAmount + if IncludeReserved then + return Cargo.storage.cargoAmount+Cargo.storage.cargoReserved + else + return Cargo.storage.cargoAmount + end else - return Cargo.storage.cargoAmount*100 -- Assume 100 kg per item + if IncludeReserved then + return Cargo.storage.cargoAmount*100 -- Assume 100 kg per item + else + return (Cargo.storage.cargoAmount+Cargo.storage.cargoReserved)*100 -- Assume 100 kg per item + end end end @@ -1737,8 +1753,9 @@ function OPSTRANSPORT:onafterStatusUpdate(From, Event, To) 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 - text=text..string.format("\n- delivered=%s [UID=%s]", - tostring(cargo.delivered), tostring(nil)) + local storage=cargo.storage + text=text..string.format("\n- storage type=%s: amount: total=%d loaded=%d, lost=%d, delivered=%d, delivered=%s [UID=%s]", + storage.cargoType, storage.cargoAmount, storage.cargoLoaded, storage.cargoLost, storage.cargoDelivered, tostring(cargo.delivered), tostring(nil)) end end @@ -2212,6 +2229,7 @@ function OPSTRANSPORT:_CreateCargoGroupData(group, TransportZoneCombo, Disembark -- Create a new data item. local cargo={} --Ops.OpsGroup#OPSGROUP.CargoGroup + cargo.uid=self.cargocounter cargo.type="OPSGROUP" cargo.opsgroup=opsgroup cargo.delivered=false @@ -2231,16 +2249,22 @@ end -- @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 #number CargoAmount Total amount of cargo that should be transported. Liquids in kg. +-- @param #number CargoWeight Weight of a single cargo item in kg. Default 1 kg. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. -- @return Ops.OpsGroup#OPSGROUP.CargoGroup Cargo group data. -function OPSTRANSPORT:_CreateCargoStorage(StorageFrom, StorageTo, CargoType, CargoAmount, TransportZoneCombo) +function OPSTRANSPORT:_CreateCargoStorage(StorageFrom, StorageTo, CargoType, CargoAmount, CargoWeight, TransportZoneCombo) local storage={} --#OPSTRANSPORT.Storage storage.storageFrom=StorageFrom storage.storageTo=StorageTo storage.cargoType=CargoType storage.cargoAmount=CargoAmount + storage.cargoDelivered=0 + storage.cargoLost=0 + storage.cargoReserved=0 + storage.cargoLoaded=0 + storage.cargoWeight=CargoWeight or 1 -- Create a new data item.