From a477fc78bcab3685665151d6536d7cdb84c6680c Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 10 Mar 2020 00:44:50 +0100 Subject: [PATCH] Ops --- .../Moose/Functional/Warehouse.lua | 69 ++++++++++++------ Moose Development/Moose/Ops/AirWing.lua | 60 ++++++++++------ Moose Development/Moose/Ops/Auftrag.lua | 48 ++++++++----- Moose Development/Moose/Ops/FlightGroup.lua | 11 ++- Moose Development/Moose/Ops/Intelligence.lua | 72 +++++++++++++++---- Moose Development/Moose/Wrapper/Unit.lua | 14 ++++ 6 files changed, 196 insertions(+), 78 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 8e8ff9fb9..fd9936718 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1608,6 +1608,8 @@ WAREHOUSE = { -- @field #boolean spawned If true, asset was spawned into the cruel world. If false, it is still in stock. -- @field #string spawngroupname Name of the spawned group. -- @field #boolean iscargo If true, asset is cargo. If false asset is transport. Nil if in stock. +-- @field #number rid The request ID of this asset. +-- @field #boolean arrived If true, asset arrived at its destination. --- Item of the warehouse queue table. -- @type WAREHOUSE.Queueitem @@ -3747,12 +3749,6 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu if asset~=nil then self:_DebugMessage(string.format("Warehouse %s: Adding KNOWN asset uid=%d with attribute=%s to stock.", self.alias, asset.uid, asset.attribute), 5) - -- Asset now belongs to this warehouse. Set warehouse ID. - asset.wid=self.uid - - -- No request associated with this asset. - asset.rid=nil - -- Set livery. if liveries then if type(liveries)=="table" then @@ -3761,13 +3757,20 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu asset.livery=liveries end end - + -- Set skill. asset.skill=skill or asset.skill + + -- Asset now belongs to this warehouse. Set warehouse ID. + asset.wid=self.uid + + -- No request associated with this asset. + asset.rid=nil -- Asset is not spawned. asset.spawned=false asset.iscargo=nil + asset.arrived=nil -- Add asset to stock. table.insert(self.stock, asset) @@ -4259,6 +4262,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Asset is transport. _assetitem.spawned=false _assetitem.iscargo=false + _assetitem.arrived=false local spawngroup=nil --Wrapper.Group#GROUP @@ -4675,6 +4679,28 @@ function WAREHOUSE:onafterUnloaded(From, Event, To, group) end end +--- On before "Arrived" event. Triggered when a group has arrived at its destination warehouse. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Group#GROUP group The group that was delivered. +function WAREHOUSE:onbeforeArrived(From, Event, To, group) + + local asset=self:FindAssetInDB(group) + + if asset then + if asset.arrived==true then + -- Asset already arrived (e.g. if multiple units trigger the event via landing). + return false + else + asset.arrived=true --ensure this is not called again from the same asset group. + return true + end + end + +end + --- On after "Arrived" event. Triggered when a group has arrived at its destination warehouse. -- The routine should be called by the warehouse sending this asset and not by the receiving warehouse. -- It is checked if this asset is cargo (or self propelled) or transport. If it is cargo it is put into the stock of receiving warehouse. @@ -4718,7 +4744,8 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) end -- Increase number of cargo delivered and transports home. - local istransport=warehouse:_GroupIsTransport(group,request) + --local istransport=warehouse:_GroupIsTransport(group,request) + if istransport==true then request.ntransporthome=request.ntransporthome+1 request.transportgroupset:Remove(group:GetName(), true) @@ -5612,11 +5639,12 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol if asset.payload then unit.payload=asset.payload.pylons env.info("FF payload") - self:I({playload=unit.payload}) - self:I(asset) + self:I({playload=unit.payload}) else env.info("FF No payload for asset!") end + env.info("FF Asset info") + self:I(asset) end @@ -5916,7 +5944,7 @@ end -- @param Wrapper.Group#GROUP group The group that arrived. -- @param #number n Waypoint passed. -- @param #number N Final waypoint. -function WAREHOUSE:_PassingWaypoint(group,n,N) +function WAREHOUSE:_PassingWaypoint(group, n, N) self:T(self.lid..string.format("Group %s passing waypoint %d of %d!", tostring(group:GetName()), n, N)) -- Final waypoint reached. @@ -6910,7 +6938,7 @@ function WAREHOUSE:_CheckRequestNow(request) if not _enough then local text=string.format("Warehouse %s: Request ID=%d denied! Not enough (cargo) assets currently available.", self.alias, request.uid) self:_InfoMessage(text, 5) - text=string.format("Enough=%s, #_assets=%d, _nassets=%d, request.nasset=%s", tostring(_enough), #_assets,_nassets, tostring(request.nasset)) + text=string.format("Enough=%s, #assets=%d, nassets=%d, request.nasset=%s", tostring(_enough), #_assets,_nassets, tostring(request.nasset)) self:T(self.lid..text) return false end @@ -7830,18 +7858,15 @@ function WAREHOUSE:_FilterStock(stock, descriptor, attribute, nmax, mobile) -- Count total number in stock. local ntot=0 - for _,_asset in ipairs(stock) do - local asset=_asset --#WAREHOUSE.Assetitem - - for _,_rasset in pairs(attribute) do - local rasset=_rasset --#WAREHOUSE.Assetitem - + for _,_rasset in pairs(attribute) do + local rasset=_rasset --#WAREHOUSE.Assetitem + for _,_asset in ipairs(stock) do + local asset=_asset --#WAREHOUSE.Assetitem if rasset.uid==asset.uid then table.insert(filtered, asset) - end - - end - + break + end + end end return filtered, #filtered, #filtered>=#attribute diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 555be6d6c..434e29d0e 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -479,6 +479,10 @@ function AIRWING:onafterStatus(From, Event, To) --self:CheckAWACS() + -- TODO: count payloads, count squadrons, count missions + local text=string.format("Status %s: missions=%d, payloads=%d, squads=%d", fsmstate, #self.missionqueue, #self.payloads, #self.squadrons) + self:I(self.lid..text) + ------------------ -- Mission Info -- ------------------ @@ -514,21 +518,14 @@ function AIRWING:onafterStatus(From, Event, To) missiontext=string.format(" [%s (%s): status=%s]", mission.type, mission.name, mission.status) end - text=text..string.format("\n -[%d] %s*%d: spawned=%s, mission=%s%s", j, typename, asset.nunits, spawned, tostring(self:IsAssetOnMission(asset)), missiontext) - - local payload=asset.payload - if payload then - text=text.." payload "..table.concat(payload.missiontypes, ", ") - else - text=text.." NO payload!" - end + text=text..string.format("\n -[%d] %s*%d \"%s\": spawned=%s, mission=%s%s", j, typename, asset.nunits, asset.spawngroupname, spawned, tostring(self:IsAssetOnMission(asset)), missiontext) + local payload=asset.payload and table.concat(asset.payload.missiontypes, ", ") or "None" + text=text.." payload="..payload end end self:I(self.lid..text) - - - + -------------- -- Mission --- -------------- @@ -651,14 +648,13 @@ function AIRWING:_GetNextMission() -- Get payload for the asset. asset.payload=self:FetchPayloadFromStock(asset.unittype, mission.type) - - env.info("FF asset payload for mission "..mission.type) - self:I(asset.payload) - + + -- Should not happen as we just checked! if not asset.payload then self:E(self.lid.."ERROR: No payload for asset! This should not happen!") end + -- Add asset to mission. mission:AddAsset(asset) end @@ -763,6 +759,17 @@ function AIRWING:onafterMissionRequest(From, Event, To, Mission) -- Add request to airwing warehouse. if #Assetlist>0 then + --local text=string.format("Requesting assets for mission %s:", Mission.name) + for i,_asset in pairs(Assetlist) do + local asset=_asset --#AIRWING.SquadronAsset + + -- Set asset to requested! Important so that new requests do not use this asset! + asset.requested=true + + --text=text..string.format("\n[%d] %s spawned=%s type=%s payload=%s", i, asset.spawngroupname, tostring(asset.spawned), asset.unittype, asset.payload and table.concat(asset.payload.missiontypes, ", ") or "no payload!") + end + --self:I(self.lid..text) + -- Add request to airwing warehouse. -- TODO: better Assignment string. self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, nil, nil, Mission.prio, tostring(Mission.auftragsnummer)) @@ -874,6 +881,9 @@ function AIRWING:onafterAssetSpawned(From, Event, To, group, asset, request) -- Create a flight group. asset.flightgroup=self:_CreateFlightGroup(asset) + -- Not requested any more. + asset.requested=nil + -- Get Mission (if any). local mission=self:GetMissionByID(request.assignment) @@ -1051,7 +1061,7 @@ function AIRWING:SquadronCanMission(Squadron, MissionType, Nassets) -- Check if the payload of this asset is compatible with the mission. if self:CheckMissionType(MissionType, asset.payload.missiontypes) then -- TODO: Check if asset actually has weapons left. Difficult! - table.insert(assets, asset) + --table.insert(assets, asset) end end @@ -1065,15 +1075,19 @@ function AIRWING:SquadronCanMission(Squadron, MissionType, Nassets) if asset.spawned then -- This asset is already spawned. Let's check if it has the right payload. if self:CheckMissionType(MissionType, asset.payload.missiontypes) then - table.insert(assets, asset) + --table.insert(assets, asset) end else - -- Check if we got a payload and reserve it for this asset. - local payload=self:FetchPayloadFromStock(asset.unittype, MissionType) - if payload then - asset.payload=payload - table.insert(assets, asset) + if not asset.requested then + + -- Check if we got a payload and reserve it for this asset. + local payload=self:FetchPayloadFromStock(asset.unittype, MissionType) + if payload then + asset.payload=payload + table.insert(assets, asset) + end + end end @@ -1215,7 +1229,7 @@ function AIRWING:CanMission(MissionType, Nassets) for _,_asset in pairs(Assets) do local asset=_asset --#AIRWING.SquadronAsset -- Only unspawned payloads are returned. - if not asset.spawned then + if not asset.spawned and not asset.requested then self:ReturnPayloadFromAsset(asset) end end diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index a2d692cf7..06fdf9378 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -62,6 +62,8 @@ -- @field #number requestID The ID of the queued warehouse request. Necessary to cancel the request if the mission was cancelled before the request is processed. -- @field #boolean cancelContactLost If true, cancel mission if the contact is lost. -- +-- @field #number missionAltitude Mission altitude in meters. +-- -- @field #number optionROE ROE. -- @field #number optionROT ROT. -- @field #number optionCM Counter measures. @@ -108,23 +110,23 @@ _AUFTRAGSNR=0 --- Mission types. -- @type AUFTRAG.Type --- @param #string ANTISHIP Anti-ship mission. --- @param #string AWACS AWACS mission. --- @param #string BAI Battlefield Air Interdiction. --- @param #string BOMBING Bombing mission. --- @param #string CAP Combat Air Patrol. --- @param #string CAS Close Air Support. --- @param #string ESCORT Escort mission. --- @param #string FACA Forward AirController airborne mission. --- @param #string FERRY Ferry flight mission. --- @param #string INTERCEPT Intercept mission. --- @param #string ORBIT Orbit mission. --- @param #string PATROL Similar to CAP but no auto engage targets. --- @param #string RECON Recon mission. --- @param #string SEAD Suppression/destruction of enemy air defences. --- @param #string STRIKE Strike mission. --- @param #string TANKER Tanker mission. --- @param #string TRANSPORT Transport mission. +-- @field #string ANTISHIP Anti-ship mission. +-- @field #string AWACS AWACS mission. +-- @field #string BAI Battlefield Air Interdiction. +-- @field #string BOMBING Bombing mission. +-- @field #string CAP Combat Air Patrol. +-- @field #string CAS Close Air Support. +-- @field #string ESCORT Escort mission. +-- @field #string FACA Forward AirController airborne mission. +-- @field #string FERRY Ferry flight mission. +-- @field #string INTERCEPT Intercept mission. +-- @field #string ORBIT Orbit mission. +-- @field #string PATROL Similar to CAP but no auto engage targets. +-- @field #string RECON Recon mission. +-- @field #string SEAD Suppression/destruction of enemy air defences. +-- @field #string STRIKE Strike mission. +-- @field #string TANKER Tanker mission. +-- @field #string TRANSPORT Transport mission. AUFTRAG.Type={ ANTISHIP="Anti Ship", AWACS="AWACS", @@ -212,9 +214,10 @@ AUFTRAG.version="0.0.5" -- TODO: Mission ROE and ROT -- TODO: Mission formation, etc. --- TODO: FSM events. +-- DONE: FSM events. -- TODO: F10 marker functions that are updated on Status event. -- TODO: Evaluate mission result ==> SUCCESS/FAILURE +-- TODO: NewAUTO() NewA2G NewA2A ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -542,6 +545,15 @@ function AUFTRAG:SetWeaponType(WeaponType) return self end +--- Set mission altitude. +-- @param #AUFTRAG self +-- @param #string Altitude Altitude in feet. +-- @return #AUFTRAG self +function AUFTRAG:SetMissionAltitude(Altitude) + self.missionAltitude=UTILS.FeetToMeters(Altitude) + return self +end + --- Set Rules of Engagement for this mission. -- @param #AUFTRAG self -- @param #number roe ROE diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 9343758ed..6bbadfb6c 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -2518,7 +2518,7 @@ end -- @param #string Event Event. -- @param #string To To state. -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. --- @param #number SpeedTo Speed used for traveling from current position to holding point in knots. Default 350 kts. +-- @param #number SpeedTo Speed used for traveling from current position to holding point in knots. Default 75% of max speed. -- @param #number SpeedHold Holding speed in knots. Default 250 kts. -- @param #number SpeedLand Landing speed in knots. Default 170 kts. function FLIGHTGROUP:onafterRTB(From, Event, To, airbase, SpeedTo, SpeedHold, SpeedLand) @@ -2526,7 +2526,7 @@ function FLIGHTGROUP:onafterRTB(From, Event, To, airbase, SpeedTo, SpeedHold, Sp self:I(self.lid..string.format("RTB: event=%s: %s --> %s", Event, From, To)) -- Defaults: - SpeedTo=SpeedTo or 350 + SpeedTo=SpeedTo or UTILS.KmphToKnots(self.speedmax*0.75) SpeedHold=SpeedHold or 250 SpeedLand=SpeedLand or 170 @@ -3324,8 +3324,13 @@ function FLIGHTGROUP:RouteToMission(mission, delay) -- Create waypoint coordinate half way between us and the target. local targetcoord=mission:GetTargetCoordinate() - local flightcoord=self.group:GetCoordinate() + local flightcoord=self.group:GetCoordinate() local waypointcoord=flightcoord:GetIntermediateCoordinate(targetcoord, 0.5) + + -- Set altitude of mission waypoint. + if mission.missionAltitude then + waypointcoord.y=mission.missionAltitude + end -- Add waypoint. self:AddWaypointAir(waypointcoord, nextwaypoint, self.speedmax*0.8, false) diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index c20407d5a..a84933280 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -17,7 +17,7 @@ -- @field #boolean Debug Debug mode. Messages to all about status. -- @field #string lid Class id string for output to DCS log file. -- @field #number coalition Coalition side number, e.g. coalition.side.RED. --- @field #table filter Category filters. +-- @field #table filterCategory Category filters. -- @field Core.Set#SET_ZONE acceptzoneset Set of accept zones. If defined, only contacts in these zones are considered. -- @field Core.Set#SET_ZONE rejectzoneset Set of reject zones. Contacts in these zones are not considered. -- @field #table Contacts Table of detected items. @@ -41,7 +41,7 @@ INTEL = { ClassName = "INTEL", Debug = nil, lid = nil, - filter = nil, + filterCategory = {}, detectionset = nil, Contacts = {}, ContactsLost = {}, @@ -202,6 +202,32 @@ function INTEL:SetForgetTime(TimeInterval) return self end +--- Filter group categories. Valid categories are: +-- +-- * Group.Category.AIRPLANE +-- * Group.Category.HELICOPTER +-- * Group.Category.GROUND +-- * Group.Category.SHIP +-- * Group.Category.TRAIN +-- +-- @param #INTEL self +-- @param #table Categories Filter categories, e.g. {Group.Category.AIRPLANE, Group.Category.HELICOPTER}. +-- @return #INTEL self +function INTEL:SetFilterCategory(Categories) + if type(Categories)~="table" then + Categories={Categories} + end + self.filterCategory=Categories + + local text="Filter categories: " + for _,category in pairs(self.filterCategory) do + text=text..string.format("%d,", category) + end + self:I(self.lid..text) + + return self +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Start & Status @@ -294,22 +320,44 @@ function INTEL:UpdateIntel() local unit=_unit --Wrapper.Unit#UNIT -- Check if unit is in any of the accept zones. - local inzone=false - for _,_zone in pairs(self.acceptzoneset.Set) do - local zone=_zone --Core.Zone#ZONE - if unit:IsInZone(zone) then - inzone=true - break + if self.acceptzoneset:Count()>0 then + local inzone=false + for _,_zone in pairs(self.acceptzoneset.Set) do + local zone=_zone --Core.Zone#ZONE + if unit:IsInZone(zone) then + inzone=true + break + end + end + + -- Unit is not in accept zone ==> remove! + if not inzone then + table.insert(remove, unit:GetName()) end end - -- Unit is not in accept zone ==> remove! - if not inzone then - table.insert(remove, unit:GetName()) - end + -- Filter unit categories. + if #self.filterCategory>0 then + local unitcategory=unit:GetCategory() + local keepit=false + for _,filtercategory in pairs(self.filterCategory) do + if unitcategory==filtercategory then + keepit=true + break + end + end + if not keepit then + self:I(self.lid..string.format("Removing unit %s category=%d", unit:GetName(), unit:GetCategory())) + table.insert(remove, unit:GetName()) + end + end + end -- Remove filtered units. + for _,unitname in pairs(remove) do + DetectedSet:Remove(unitname, true) + end --DetectedSet:RemoveUnitsByName(remove) -- Create detected contacts. diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 3decd402f..dcbea776a 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -756,6 +756,20 @@ function UNIT:GetDamageRelative() return 1 end +--- Returns the category of the #UNIT. +-- @param #UNIT self +-- @return #number Unit category. +function UNIT:GetCategory() + self:F3( self.UnitName ) + + local DCSUnit = self:GetDCSObject() + if DCSUnit then + return DCSUnit:getDesc().category + end + + return nil +end + --- Returns the category name of the #UNIT. -- @param #UNIT self -- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship