diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index a602549a3..e1fb8d8b2 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -39,7 +39,7 @@ -- @field #number markerid ID of the warehouse marker at the airbase. -- @field #number dTstatus Time interval in seconds of updating the warehouse status and processing new events. Default 30 seconds. -- @field #number assetid Unique id of asset items in stock. Essentially a running number starting at one and incremented when a new asset is added. --- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Stockitem}. +-- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Assetitem}. -- @field #table queue Table holding all queued requests. Table entries are of type @{#WAREHOUSE.Queueitem}. -- @field #table pending Table holding all pending requests, i.e. those that are currently in progress. Table entries are of type @{#WAREHOUSE.Pendingitem}. -- @extends Core.Fsm#FSM @@ -113,7 +113,7 @@ WAREHOUSE = { } --- Item of the warehouse stock table. --- @type WAREHOUSE.Stockitem +-- @type WAREHOUSE.Assetitem -- @field #number uid Unique id of the asset. -- @field #string templatename Name of the template group. -- @field #table template The spawn template of the group. @@ -121,7 +121,9 @@ WAREHOUSE = { -- @field #string unittype Type of the first unit of the group as obtained by the Object.getTypeName() DCS API function. -- @field #number nunits Number of units in the group. -- @field #number range Range of the unit in meters. +-- @field #number size Maximum size in length and with of the asset in meters. -- @field #number speedmax Maximum speed in km/h the unit can do. +-- @field DCS#Object.Desc DCSdesc All DCS descriptors. -- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. --- Item of the warehouse queue table. @@ -140,7 +142,7 @@ WAREHOUSE = { --- Item of the warehouse pending queue table. -- @type WAREHOUSE.Pendingitem -- @extends #WAREHOUSE.Queueitem --- @field #table assets Table of assets - cargo or transport. Each element of the table is a @{#WAREHOUSE.Stockitem}. +-- @field #table assets Table of assets - cargo or transport. Each element of the table is a @{#WAREHOUSE.Assetitem}. -- @field Core.Set#SET_GROUP cargogroupset Set of cargo groups do be delivered. -- @field #number ndelivered Number of groups delivered to destination. -- @field #number cargoattribute Attribute of cargo assets of type @{#WAREHOUSE.Attribute}. @@ -150,19 +152,23 @@ WAREHOUSE = { -- @field #number transportcategory Category of transport assets of type @{#WAREHOUSE.Category}. -- @field #number ntransporthome Number of transports back home. transportattribute ---- Descriptors enumerator describing the type of the asset in stock. +--- Descriptors enumerator describing the type of the asset. -- @type WAREHOUSE.Descriptor +-- @field #string TEMPLATENAME Name of the asset template. +-- @field #string UNITTYPE Typename of the DCS unit. +-- @field #string ATTRIBUTE Generalized attribute @{#WAREHOUSE.Attribute}. +-- @field #string CATEGORY Asset category of type DCS#Group.Category, i.e. GROUND, AIRPLANE, HELICOPTER, SHIP, TRAIN. WAREHOUSE.Descriptor = { - ID="id", + --ID="id", TEMPLATENAME="templatename", - CATEGORY="category", UNITTYPE="unittype", ATTRIBUTE="attribute", + CATEGORY="category", } --- Warehouse generalited categories. -- @type WAREHOUSE.Attribute --- @field #string TRANSPORT_PLANE Airplane with transport capability. Usually bigger. +-- @field #string TRANSPORT_PLANE Airplane with transport capability. Usually bigger, i.e. needs larger airbases and parking spots. -- @field #string TRANSPORT_HELO Helicopter with transport capability. WAREHOUSE.Attribute = { TRANSPORT_PLANE="Transport_Plane", @@ -193,9 +199,18 @@ WAREHOUSE.TransportType = { SELFPROPELLED = "Selfpropelled", } +--- Warehouse database. Note that this is a global array to have easier exchange between warehouses. +-- @type WAREHOUSE.db +-- @field #number AssetID Unique ID of each asset. This is a running number, which is increased each time a new asset is added. +-- @field #table Assets Table holding registered assets, which are of type @{Functional.Warehouse#WAREHOUSE.Assetitem}. +WAREHOUSE.db = { + AssetID = 0, + Assets = {}, +} + --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.2" +WAREHOUSE.version="0.2.2w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -220,6 +235,7 @@ WAREHOUSE.version="0.2.2" -- TODO: Add general message function for sending to coaliton or debug. -- TODO: Use RAT for routing air units. Should be possible but might need some modifications of RAT, e.g. explit spawn place. But flight plan should be better. -- TODO: Can I make a request with specific assets? E.g., once delivered, make a request for exactly those assests that were in the original request. +-- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -515,7 +531,7 @@ function WAREHOUSE:IsAttacked() return self:is("Attacked") end ---- Check if the warehouse has a road connection to another warehouse. Both warehouses need to be running! +--- Check if the warehouse has a road connection to another warehouse. Both warehouses need to be started! -- @param #WAREHOUSE self -- @param #WAREHOUSE warehouse The remote warehose to where the connection is checked. -- @param #boolean markpath If true, place markers of path segments on the F10 map. @@ -535,6 +551,26 @@ function WAREHOUSE:HasConnectionRoad(warehouse, markpath, smokepath) return nil, -1 end +--- Check if the warehouse has a railroad connection to another warehouse. Both warehouses need to be started! +-- @param #WAREHOUSE self +-- @param #WAREHOUSE warehouse The remote warehose to where the connection is checked. +-- @param #boolean markpath If true, place markers of path segments on the F10 map. +-- @param #boolean smokepath If true, put green smoke on path segments. +-- @return #boolean If true, the two warehouses are connected by road. +-- @return #number Path length in meters. Negative distance -1 meter indicates no connection. +function WAREHOUSE:HasConnectionRail(warehouse, markpath, smokepath) + if warehouse then + if self.rail and warehouse.rail then + local _,length,gotpath=self.road:GetPathOnRoad(warehouse.road, false, true, markpath, smokepath) + return gotpath, length or -1 + else + -- At least one of the warehouses has no rail connection. + return false, -1 + end + end + return nil, -1 +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM states ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -568,8 +604,7 @@ function WAREHOUSE:onafterStart(From, Event, To) -- Debug mark warehouse & spawn zone. self.zone:BoundZone(30, self.country) self.spawnzone:BoundZone(30, self.country) - - --self.spawnzone:GetCoordinate():MarkToAll("Spawnzone of warehouse "..self.alias) + -- Get the closest point on road wrt spawnzone of ground assets. local _road=self.spawnzone:GetCoordinate():GetClosestPointToRoad() @@ -625,7 +660,7 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStop(From, Event, To) - self:E(self.wid..string.format("Warehouse stopped")) + self:E(self.wid..string.format("Warehouse stopped!")) -- Unhandle event. self:UnHandleEvent(EVENTS.Birth) @@ -637,6 +672,9 @@ function WAREHOUSE:onafterStop(From, Event, To) self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.BaseCaptured) + -- Clear all pending schedules. + self.CallScheduler:Clear() + end --- On after "Pause" event. Pauses the warehouse, i.e. no requests are processed. However, new requests and new assets can be added in this state. @@ -645,7 +683,7 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterPause(From, Event, To) - self:E(self.wid..string.format("Warehouse paused! Queued requests are not processed in this state.")) + self:E(self.wid..string.format("Warehouse %s paused! Queued requests are not processed in this state.", self.alias)) end --- On after "Unpause" event. Unpauses the warehouse, i.e. requests in queue are processed again. @@ -665,7 +703,7 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStatus(From, Event, To) - self:E(self.wid..string.format("Checking warehouse status of %s", self.alias)) + self:E(self.wid..string.format("Checking status of warehouse %s.", self.alias)) -- Print status. self:_DisplayStatus() @@ -717,61 +755,90 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups) -- Set default. local n=ngroups or 1 - local asset --Wrapper.Group#GROUP - if type(group)=="string" then - env.info("New asset passed as string") - asset=GROUP:FindByName(group) - elseif type(group)=="table" and group.aid~=nil then - env.info("New asset passed as asset") - self:_AddExistingAsset(group) - return - elseif group:IsInstanceOf("GROUP") then - env.info("New asset passed as string") - asset=group - else - self:E("ERROR: Invalid asset added!") - return nil - end - - - -- Check if group exists and has a DCS object. - -- TODO: Need to check this carefully if this words with CARGO etc. - if asset and asset:IsAlive()~=nil and asset:GetDCSObject() then - - -- Add new asset. - self:_AddNewAsset(asset, ngroups) + if group then + + -- Get unique ids from group name. + local wid,aid,rid=self:_GetIDsFromGroup(group) + + -- Check if this is an known or a new asset group. + + if aid~=nil and wid~=nil then + -- We got a warehouse and asset id ==> this is an "old" group. + local asset=self:_FindAssetInDB(group) + + -- Note the group is only added once, i.e. the ngroups parameter is ignored here. + -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. + if asset~=nil then + table.insert(self.stock, asset) + end + + else + + -- This is a group that is not in the db yet. Add it n times. + local assets=self:_RegisterAsset(group, n) + + -- Add created assets to stock of this warehouse. + for _,asset in pairs(assets) do + table.insert(self.stock, asset) + end + end -- Destroy group if it is alive. + -- TODO: This causes a problem, when a completely new asset is added, i.e. not from a template group. + -- Need to create a "zombie" template group maybe? if group:IsAlive()==true then group:Destroy() end - - else - -- Group name does not exist! - self:E(string.format("ERROR: Template group name not defined in the mission editor. Check the spelling! templategroupname=%s",tostring(templategroupname))) + end - + end ---- Add an existing asset to the warehouse stock. +--- Find an asset in the the global warehouse db. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Stockitem asset The asset to be added. -function WAREHOUSE:_AddExistingAsset(asset) - table.insert(self.stock, asset) +-- @param Wrapper.Group#GROUP group The group from which it is assumed that it has a registered asset. +-- @return #WAREHOUSE.Assetitem The asset from the data base or nil if it could not be found. +function WAREHOUSE:_FindAssetInDB(group) + -- Get unique ids from group name. + local wid,aid,rid=self:_GetIDsFromGroup(group) + + if aid~=nil then + local asset=WAREHOUSE.db.Assets[aid] + if asset==nil then + self:E(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName())) + end + return asset + end + + self:E(string.format("ERROR: Group %s does not contain an asset ID in its name!", group:GetName())) + return nil end ---- Add a completely new asset to the warehouse. +--- Register new asset in globase warehouse data base. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group that will be added to the warehouse stock. -- @param #number ngroups Number of groups to be added. -function WAREHOUSE:_AddNewAsset(group, ngroups) +-- @return #table A table containing all registered assets. +function WAREHOUSE:_RegisterAsset(group, ngroups) -- Set default. local n=ngroups or 1 + -- Get the size of an object. + local function _GetObjectSize(DCSdesc) + if DCSdesc.box then + local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) + local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y) --height + local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) + return math.max(x,z), x , y, z + end + return 0,0,0,0 + end + local templategroupname=group:GetName() local DCSgroup=group:GetDCSObject() + local DCSunit=DCSgroup:getUnit(1) local DCSdesc=DCSunit:getDesc() local DCSdisplay=DCSdesc.displayName @@ -779,45 +846,61 @@ function WAREHOUSE:_AddNewAsset(group, ngroups) local DCStype=DCSunit:getTypeName() local SpeedMax=group:GetSpeedMax() local RangeMin=group:GetRange() - group:GetCategory() - - env.info(string.format("New asset for warehouse %s:", self.alias)) - env.info(string.format("Group name = %s", group:GetName())) - env.info(string.format("Display name = %s", DCSdisplay)) - env.info(string.format("Category = %s", DCScategory)) - env.info(string.format("Type = %s", DCStype)) - env.info(string.format("Speed max = %s km/h", tostring(SpeedMax))) - env.info(string.format("Range min = %s m", tostring(RangeMin))) - self:E({fullassetdesc=DCSdesc}) + local smax,sx,sy,sz=_GetObjectSize(DCSdesc) -- Get the generalized attribute. local attribute=self:_GetAttribute(templategroupname) + -- Table for returned assets. + local assets={} + -- Add this n times to the table. for i=1,n do - local stockitem={} --#WAREHOUSE.Stockitem + local asset={} --#WAREHOUSE.Assetitem -- Increase asset unique id counter. self.assetid=self.assetid+1 + WAREHOUSE.db.AssetID=WAREHOUSE.db.AssetID+1 -- Set parameters. - stockitem.uid=self.assetid - stockitem.templatename=templategroupname - stockitem.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) - stockitem.category=DCScategory - stockitem.unittype=DCStype - stockitem.attribute=attribute - stockitem.range=RangeMin - stockitem.speedmax=SpeedMax + asset.uid=WAREHOUSE.db.AssetID + asset.templatename=templategroupname + asset.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) + asset.category=DCScategory + asset.unittype=DCStype + asset.nunits=#asset.template.units + asset.attribute=attribute + asset.range=RangeMin + asset.speedmax=SpeedMax + asset.size=smax + asset.DCSdesc=DCSdesc - -- Modify the template so that the group is spawned with the right coalition. - -- TODO: somehow this is now acknoleged properly. Found a workaround however with SPAWN AIP functions. - stockitem.template.CoalitionID=self.coalition - stockitem.template.CountryID=self.country + if i==1 then + env.info(string.format("New asset for warehouse %s:", self.alias)) + env.info(string.format("Group name = %s", group:GetName())) + env.info(string.format("Display name = %s", DCSdisplay)) + env.info(string.format("Category = %s", DCScategory)) + env.info(string.format("Type = %s", DCStype)) + env.info(string.format("Size max = %s", asset.size)) + env.info(string.format("Speed max = %s km/h", SpeedMax)) + env.info(string.format("Range min = %s m", RangeMin)) + self:E({fullassetdesc=DCSdesc}) + end - table.insert(self.stock, stockitem) + -- Add asset to global db. + table.insert(WAREHOUSE.db.Assets, {uid=asset.uid, asset=asset}) + + table.insert(assets,asset) end + return assets +end + +--- Asset item characteristics. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Assetitem asset +function WAREHOUSE:_AssetItemInfo(asset) + end --- On after "AddAsset" event. Add a group to the warehouse stock. If the group is alive, it is destroyed. @@ -831,7 +914,7 @@ end --- Spawn a ground asset in the spawnzone of the warehouse. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Stockitem asset Ground asset that will be spawned. +-- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. function WAREHOUSE:_SpawnAssetGround(asset, request) @@ -887,7 +970,7 @@ end --- Spawn an aircraft asset (plane or helo) at the airbase associated with the warehouse. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Stockitem asset Ground asset that will be spawned. +-- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @param #table parking Parking data for this asset. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. @@ -900,8 +983,9 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking) -- Set and empty route. if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then - --template.route.points=self:_GetFlightplan(group,_departure,_destination) - template.route.points[1] = {} + -- Get flight path. + template.route.points=self:_GetFlightplan(asset, self.airbase, request.warehouse.airbase) + --template.route.points[1] = {} else template.route.points[1] = {} end @@ -1013,9 +1097,9 @@ end --- Prepare a spawn template for the asset. Deep copy of asset template, adjusting template and unit names, nillifying group and unit ids. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Stockitem asset Ground asset that will be spawned. +-- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. --- @return #template Prepared new spawn template. +-- @return #table Prepared new spawn template. function WAREHOUSE:_SpawnAssetPrepareTemplate(asset, request) -- Create an own copy of the template! @@ -1127,7 +1211,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) -- Check if destination is in range for all requested assets. for _,_asset in pairs(_assets) do - local asset=_asset --#WAREHOUSE.Stockitem + local asset=_asset --#WAREHOUSE.Assetitem -- Check if destination is in range. if asset.range