This commit is contained in:
Frank 2020-08-17 01:24:51 +02:00
parent 3ea8b3737f
commit 2a4f6020c2
10 changed files with 535 additions and 327 deletions

View File

@ -46,6 +46,7 @@
-- @type WAREHOUSE
-- @field #string ClassName Name of the class.
-- @field #boolean Debug If true, send debug messages to all.
-- @field #number verbose Verbosity level.
-- @field #string wid Identifier of the warehouse printed before other output to DCS.log file.
-- @field #boolean Report If true, send status messages to coalition.
-- @field Wrapper.Static#STATIC warehouse The phyical warehouse structure.
@ -1830,14 +1831,12 @@ WAREHOUSE.version="1.0.2"
-- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static
-- @return #WAREHOUSE self
function WAREHOUSE:New(warehouse, alias)
BASE:T({warehouse=warehouse})
-- Check if just a string was given and convert to static.
if type(warehouse)=="string" then
local warehousename=warehouse
warehouse=UNIT:FindByName(warehousename)
if warehouse==nil then
--env.info(string.format("No warehouse unit with name %s found trying static.", tostring(warehousename)))
warehouse=STATIC:FindByName(warehousename, true)
self.isunit=false
else
@ -1858,7 +1857,7 @@ function WAREHOUSE:New(warehouse, alias)
env.info(string.format("Adding warehouse v%s for structure %s with alias %s", WAREHOUSE.version, warehouse:GetName(), self.alias))
-- Inherit everthing from FSM class.
local self = BASE:Inherit(self, FSM:New()) -- #WAREHOUSE
local self=BASE:Inherit(self, FSM:New()) -- #WAREHOUSE
-- Set some string id for output to DCS.log file.
self.lid=string.format("WAREHOUSE %s | ", self.alias)
@ -1890,6 +1889,8 @@ function WAREHOUSE:New(warehouse, alias)
-- Defaults
self:SetMarker(true)
self:SetReportOff()
self:SetVerbosity(0)
-- Add warehouse to database.
_WAREHOUSEDB.Warehouses[self.uid]=self
@ -2551,6 +2552,15 @@ function WAREHOUSE:SetStatusUpdate(timeinterval)
return self
end
--- Set verbosity level.
-- @param #WAREHOUSE self
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
-- @return #WAREHOUSE self
function WAREHOUSE:SetVerbosity(VerbosityLevel)
self.verbose=VerbosityLevel or 0
return self
end
--- Set a zone where the (ground) assets of the warehouse are spawned once requested.
-- @param #WAREHOUSE self
-- @param Core.Zone#ZONE zone The spawn zone.
@ -3253,16 +3263,6 @@ function WAREHOUSE:onafterStart(From, Event, To)
-- Save self in static object. Easier to retrieve later.
self.warehouse:SetState(self.warehouse, "WAREHOUSE", self)
-- THIS! caused aircraft to be spawned and started but they would never begin their route!
-- VERY strange. Need to test more.
--[[
-- Debug mark warehouse & spawn zone.
self.zone:BoundZone(30, self.country)
self.spawnzone:BoundZone(30, self.country)
]]
--self.spawnzone:GetCoordinate():MarkToCoalition(string.format("Warehouse %s spawn zone", self.alias), self:GetCoalition())
-- Get the closest point on road wrt spawnzone of ground assets.
local _road=self.spawnzone:GetCoordinate():GetClosestPointToRoad()
if _road and self.road==nil then
@ -3402,13 +3402,17 @@ end
-- @param #string To To state.
function WAREHOUSE:onafterStatus(From, Event, To)
local FSMstate=self:GetState()
local coalition=self:GetCoalitionName()
local country=self:GetCountryName()
-- Info.
self:I(self.lid..string.format("State=%s %s [%s]: Assets=%d, Requests: waiting=%d, pending=%d", FSMstate, country, coalition, #self.stock, #self.queue, #self.pending))
-- General info.
if self.verbose>=1 then
local FSMstate=self:GetState()
local coalition=self:GetCoalitionName()
local country=self:GetCountryName()
-- Info.
self:I(self.lid..string.format("State=%s %s [%s]: Assets=%d, Requests: waiting=%d, pending=%d", FSMstate, country, coalition, #self.stock, #self.queue, #self.pending))
end
-- Check if any pending jobs are done and can be deleted from the queue.
self:_JobDone()
@ -7616,10 +7620,10 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
local problem=nil
-- Safe parking using TO_AC from DCS result.
self:I(self.lid..string.format("Parking spot %d TOAC=%s (safe park=%s).", _termid, tostring(_toac), tostring(self.safeparking)))
self:T2(self.lid..string.format("Parking spot %d TOAC=%s (safe park=%s)", _termid, tostring(_toac), tostring(self.safeparking)))
if self.safeparking and _toac then
free=false
self:I(self.lid..string.format("Parking spot %d is occupied by other aircraft taking off (TOAC).", _termid))
self:T(self.lid..string.format("Parking spot %d is occupied by other aircraft taking off (TOAC)", _termid))
end
-- Loop over all obstacles.
@ -7648,7 +7652,8 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
-- Add parkingspot for this asset unit.
table.insert(parking[_asset.uid], parkingspot)
self:I(self.lid..string.format("Parking spot %d is free for asset id=%d!", _termid, _asset.uid))
-- Debug
self:T(self.lid..string.format("Parking spot %d is free for asset id=%d!", _termid, _asset.uid))
-- Add the unit as obstacle so that this spot will not be available for the next unit.
table.insert(obstacles, {coord=_spot, size=_asset.size, name=_asset.templatename, type="asset"})
@ -7659,7 +7664,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
else
-- Debug output for occupied spots.
self:I(self.lid..string.format("Parking spot %d is occupied or not big enough!", _termid))
self:T(self.lid..string.format("Parking spot %d is occupied or not big enough!", _termid))
if self.Debug then
local coord=problem.coord --Core.Point#COORDINATE
local text=string.format("Obstacle blocking spot #%d is %s type %s with size=%.1f m and distance=%.1f m.", _termid, problem.name, problem.type, problem.size, problem.dist)
@ -8284,67 +8289,70 @@ end
-- @param #string name Name of the queue for info reasons.
function WAREHOUSE:_PrintQueue(queue, name)
local total="Empty"
if #queue>0 then
total=string.format("Total = %d", #queue)
end
if self.verbose>=2 then
-- Init string.
local text=string.format("%s at %s: %s",name, self.alias, total)
for i,qitem in ipairs(queue) do
local qitem=qitem --#WAREHOUSE.Pendingitem
local uid=qitem.uid
local prio=qitem.prio
local clock="N/A"
if qitem.timestamp then
clock=tostring(UTILS.SecondsToClock(qitem.timestamp))
local total="Empty"
if #queue>0 then
total=string.format("Total = %d", #queue)
end
local assignment=tostring(qitem.assignment)
local requestor=qitem.warehouse.alias
local airbasename=qitem.warehouse:GetAirbaseName()
local requestorAirbaseCat=qitem.warehouse:GetAirbaseCategory()
local assetdesc=qitem.assetdesc
local assetdescval=qitem.assetdescval
if assetdesc==WAREHOUSE.Descriptor.ASSETLIST then
assetdescval="Asset list"
-- Init string.
local text=string.format("%s at %s: %s",name, self.alias, total)
for i,qitem in ipairs(queue) do
local qitem=qitem --#WAREHOUSE.Pendingitem
local uid=qitem.uid
local prio=qitem.prio
local clock="N/A"
if qitem.timestamp then
clock=tostring(UTILS.SecondsToClock(qitem.timestamp))
end
local assignment=tostring(qitem.assignment)
local requestor=qitem.warehouse.alias
local airbasename=qitem.warehouse:GetAirbaseName()
local requestorAirbaseCat=qitem.warehouse:GetAirbaseCategory()
local assetdesc=qitem.assetdesc
local assetdescval=qitem.assetdescval
if assetdesc==WAREHOUSE.Descriptor.ASSETLIST then
assetdescval="Asset list"
end
local nasset=tostring(qitem.nasset)
local ndelivered=tostring(qitem.ndelivered)
local ncargogroupset="N/A"
if qitem.cargogroupset then
ncargogroupset=tostring(qitem.cargogroupset:Count())
end
local transporttype="N/A"
if qitem.transporttype then
transporttype=qitem.transporttype
end
local ntransport="N/A"
if qitem.ntransport then
ntransport=tostring(qitem.ntransport)
end
local ntransportalive="N/A"
if qitem.transportgroupset then
ntransportalive=tostring(qitem.transportgroupset:Count())
end
local ntransporthome="N/A"
if qitem.ntransporthome then
ntransporthome=tostring(qitem.ntransporthome)
end
-- Output text:
text=text..string.format(
"\n%d) UID=%d, Prio=%d, Clock=%s, Assignment=%s | Requestor=%s [Airbase=%s, category=%d] | Assets(%s)=%s: #requested=%s / #alive=%s / #delivered=%s | Transport=%s: #requested=%s / #alive=%s / #home=%s",
i, uid, prio, clock, assignment, requestor, airbasename, requestorAirbaseCat, assetdesc, assetdescval, nasset, ncargogroupset, ndelivered, transporttype, ntransport, ntransportalive, ntransporthome)
end
local nasset=tostring(qitem.nasset)
local ndelivered=tostring(qitem.ndelivered)
local ncargogroupset="N/A"
if qitem.cargogroupset then
ncargogroupset=tostring(qitem.cargogroupset:Count())
end
local transporttype="N/A"
if qitem.transporttype then
transporttype=qitem.transporttype
end
local ntransport="N/A"
if qitem.ntransport then
ntransport=tostring(qitem.ntransport)
end
local ntransportalive="N/A"
if qitem.transportgroupset then
ntransportalive=tostring(qitem.transportgroupset:Count())
end
local ntransporthome="N/A"
if qitem.ntransporthome then
ntransporthome=tostring(qitem.ntransporthome)
end
-- Output text:
text=text..string.format(
"\n%d) UID=%d, Prio=%d, Clock=%s, Assignment=%s | Requestor=%s [Airbase=%s, category=%d] | Assets(%s)=%s: #requested=%s / #alive=%s / #delivered=%s | Transport=%s: #requested=%s / #alive=%s / #home=%s",
i, uid, prio, clock, assignment, requestor, airbasename, requestorAirbaseCat, assetdesc, assetdescval, nasset, ncargogroupset, ndelivered, transporttype, ntransport, ntransportalive, ntransporthome)
end
if #queue==0 then
self:T(self.lid..text)
else
if total~="Empty" then
if #queue==0 then
self:I(self.lid..text)
else
if total~="Empty" then
self:I(self.lid..text)
end
end
end
end
@ -8352,17 +8360,19 @@ end
--- Display status of warehouse.
-- @param #WAREHOUSE self
function WAREHOUSE:_DisplayStatus()
local text=string.format("\n------------------------------------------------------\n")
text=text..string.format("Warehouse %s status: %s\n", self.alias, self:GetState())
text=text..string.format("------------------------------------------------------\n")
text=text..string.format("Coalition name = %s\n", self:GetCoalitionName())
text=text..string.format("Country name = %s\n", self:GetCountryName())
text=text..string.format("Airbase name = %s (category=%d)\n", self:GetAirbaseName(), self:GetAirbaseCategory())
text=text..string.format("Queued requests = %d\n", #self.queue)
text=text..string.format("Pending requests = %d\n", #self.pending)
text=text..string.format("------------------------------------------------------\n")
text=text..self:_GetStockAssetsText()
self:T(text)
if self.verbose>=3 then
local text=string.format("\n------------------------------------------------------\n")
text=text..string.format("Warehouse %s status: %s\n", self.alias, self:GetState())
text=text..string.format("------------------------------------------------------\n")
text=text..string.format("Coalition name = %s\n", self:GetCoalitionName())
text=text..string.format("Country name = %s\n", self:GetCountryName())
text=text..string.format("Airbase name = %s (category=%d)\n", self:GetAirbaseName(), self:GetAirbaseCategory())
text=text..string.format("Queued requests = %d\n", #self.queue)
text=text..string.format("Pending requests = %d\n", #self.pending)
text=text..string.format("------------------------------------------------------\n")
text=text..self:_GetStockAssetsText()
self:I(text)
end
end
--- Get text about warehouse stock.

View File

@ -204,6 +204,7 @@ function AIRWING:New(warehousename, airwingname)
self:AddTransition("*", "FlightOnMission", "*") -- Flight was spawned with a mission.
-- Defaults:
self:SetVerbosity(0)
self.nflightsCAP=0
self.nflightsAWACS=0
self.nflightsTANKERboom=0
@ -559,6 +560,15 @@ function AIRWING:GetSquadron(SquadronName)
return nil
end
--- Set verbosity level.
-- @param #AIRWING self
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
-- @return #AIRWING self
function AIRWING:SetVerbosity(VerbosityLevel)
self.verbose=VerbosityLevel or 0
return self
end
--- Get squadron of an asset.
-- @param #AIRWING self
-- @param #AIRWING.SquadronAsset Asset The squadron asset.
@ -792,93 +802,52 @@ function AIRWING:onafterStatus(From, Event, To)
-- Check Rescue Helo missions.
self:CheckRescuhelo()
-- Count missions not over yet.
local nmissions=self:CountMissionsInQueue()
-- Count ALL payloads in stock. If any payload is unlimited, this gives 999.
local Npayloads=self:CountPayloadsInStock(AUFTRAG.Type)
-- General info:
-- TODO: assets total
local text=string.format("%s: Missions=%d, Payloads=%d (%d), Squads=%d", fsmstate, nmissions, Npayloads, #self.payloads, #self.squadrons)
self:I(self.lid..text)
if self.verbose>=1 then
-- Count missions not over yet.
local nmissions=self:CountMissionsInQueue()
-- Count ALL payloads in stock. If any payload is unlimited, this gives 999.
local Npayloads=self:CountPayloadsInStock(AUFTRAG.Type)
-- TODO: assets total
-- Output.
local text=string.format("%s: Missions=%d, Payloads=%d (%d), Squads=%d", fsmstate, nmissions, Npayloads, #self.payloads, #self.squadrons)
self:I(self.lid..text)
end
------------------
-- Mission Info --
------------------
local text=string.format("Missions Total=%d:", #self.missionqueue)
for i,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
text=text..string.format("\n[%d] %s: Status=%s, Nassets=%d, Prio=%d, ID=%d (%s)", i, mission.type, mission.status, mission.nassets, mission.prio, mission.auftragsnummer, mission.name)
if self.verbose>=2 then
local text=string.format("Missions Total=%d:", #self.missionqueue)
for i,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
text=text..string.format("\n[%d] %s: Status=%s, Nassets=%d, Prio=%d, ID=%d (%s)", i, mission.type, mission.status, mission.nassets, mission.prio, mission.auftragsnummer, mission.name)
end
self:I(self.lid..text)
end
self:I(self.lid..text)
-------------------
-- Squadron Info --
-------------------
local text="Squadrons:"
for i,_squadron in pairs(self.squadrons) do
local squadron=_squadron --Ops.Squadron#SQUADRON
local callsign=squadron.callsignName and UTILS.GetCallsignName(squadron.callsignName) or "N/A"
local modex=squadron.modex and squadron.modex or -1
local skill=squadron.skill and tostring(squadron.skill) or "N/A"
-- Squadron text
text=text..string.format("\n* %s %s: %s*%d/%d, Callsign=%s, Modex=%d, Skill=%s", squadron.name, squadron:GetState(), squadron.aircrafttype, squadron:CountAssetsInStock(), #squadron.assets, callsign, modex, skill)
-- Loop over all assets.
if self.verbose>1 then
for j,_asset in pairs(squadron.assets) do
local asset=_asset --#AIRWING.SquadronAsset
local assignment=asset.assignment or "none"
local name=asset.templatename
local spawned=tostring(asset.spawned)
local groupname=asset.spawngroupname
local typename=asset.unittype
local mission=self:GetAssetCurrentMission(asset)
local missiontext=""
if mission then
local distance=asset.flightgroup and UTILS.MetersToNM(mission:GetTargetDistance(asset.flightgroup.group:GetCoordinate())) or 0
missiontext=string.format(" [%s (%s): status=%s, distance=%.1f NM]", mission.type, mission.name, mission.status, distance)
end
-- Mission info.
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)
-- Payload info.
local payload=asset.payload and table.concat(self:GetPayloadMissionTypes(asset.payload), ", ") or "None"
text=text.." payload="..payload
-- Flight status.
text=text..", flight: "
if asset.flightgroup and asset.flightgroup:IsAlive() then
local status=asset.flightgroup:GetState()
local fuelmin=asset.flightgroup:GetFuelMin()
local fuellow=asset.flightgroup:IsFuelLow()
local fuelcri=asset.flightgroup:IsFuelCritical()
text=text..string.format("%s fuel=%d", status, fuelmin)
if fuelcri then
text=text.." (critical!)"
elseif fuellow then
text=text.." (low)"
end
local lifept, lifept0=asset.flightgroup:GetLifePoints()
text=text..string.format(" life=%d/%d", lifept, lifept0)
local ammo=asset.flightgroup:GetAmmoTot()
text=text..string.format(" ammo=[G=%d, R=%d, B=%d, M=%d]", ammo.Guns, ammo.Rockets, ammo.Bombs, ammo.Missiles)
else
text=text.."N/A"
end
end
if self.verbose>=3 then
local text="Squadrons:"
for i,_squadron in pairs(self.squadrons) do
local squadron=_squadron --Ops.Squadron#SQUADRON
local callsign=squadron.callsignName and UTILS.GetCallsignName(squadron.callsignName) or "N/A"
local modex=squadron.modex and squadron.modex or -1
local skill=squadron.skill and tostring(squadron.skill) or "N/A"
-- Squadron text
text=text..string.format("\n* %s %s: %s*%d/%d, Callsign=%s, Modex=%d, Skill=%s", squadron.name, squadron:GetState(), squadron.aircrafttype, squadron:CountAssetsInStock(), #squadron.assets, callsign, modex, skill)
end
self:I(self.lid..text)
end
self:I(self.lid..text)
--------------
-- Mission ---

View File

@ -98,8 +98,9 @@
--
-- @field #table enrouteTasks Mission enroute tasks.
--
-- @field #number missionRepeated Number of times mission was repeated.
-- @field #number missionRepeatMax Number of times mission is repeated if failed.
-- @field #number repeated Number of times mission was repeated.
-- @field #number NrepeatFailure Number of times mission is repeated if failed.
-- @field #number NrepeatSuccess Number of times mission is repeated if successful.
--
-- @field Ops.OpsGroup#OPSGROUP.Radio radio Radio freq and modulation.
-- @field Ops.OpsGroup#OPSGROUP.Beacon tacan TACAN setting.
@ -259,7 +260,7 @@
AUFTRAG = {
ClassName = "AUFTRAG",
Debug = false,
verbose = 2,
verbose = 0,
lid = nil,
auftragsnummer = nil,
groupdata = {},
@ -473,19 +474,20 @@ function AUFTRAG:New(Type)
-- Auftragsnummer.
self.auftragsnummer=_AUFTRAGSNR
-- Log id.
-- Log ID.
self:_SetLogID()
-- State is planned.
self.status=AUFTRAG.Status.PLANNED
-- Defaults
self:SetVerbosity(0)
self:SetName()
self:SetPriority()
self:SetTime()
self.engageAsGroup=true
self.missionRepeated=0
self.missionRepeatMax=0
self.repeated=0
self.NrepeatFailure=0
self.NrepeatSuccess=0
self.nassets=1
self.dTevaluate=0
@ -1413,16 +1415,16 @@ function AUFTRAG:SetPriority(Prio, Urgent)
return self
end
--- Set how many times the mission is repeated if it fails.
--- Set how many times the mission is repeated if it fails. Only valid if the mission is handled by an AIRWING or higher level.
-- @param #AUFTRAG self
-- @param #number Nrepeat Number of repeats. Default 0.
-- @return #AUFTRAG self
function AUFTRAG:SetRepeatOnFailure(Nrepeat)
self.missionRepeatMax=Nrepeat or 0
self.NrepeatFailure=Nrepeat or 0
return self
end
--- Set how many times the mission is repeated if it was successful.
--- Set how many times the mission is repeated if it was successful. Only valid if the mission is handled by an AIRWING or higher level.
-- @param #AUFTRAG self
-- @param #number Nrepeat Number of repeats. Default 0.
-- @return #AUFTRAG self
@ -1431,7 +1433,7 @@ function AUFTRAG:SetRepeatOnSuccess(Nrepeat)
return self
end
--- Define how many assets are required to do the job.
--- Define how many assets are required to do the job. Only valid if the mission is handled by an AIRWING or higher level.
-- @param #AUFTRAG self
-- @param #number Nassets Number of asset groups. Default 1.
-- @return #AUFTRAG self
@ -1459,6 +1461,15 @@ function AUFTRAG:SetEnableMarkers(Coalition)
return self
end
--- Set verbosity level.
-- @param #AUFTRAG self
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
-- @return #AUFTRAG self
function AUFTRAG:SetVerbosity(VerbosityLevel)
self.verbose=VerbosityLevel or 0
return self
end
--- Set weapon type used for the engagement.
-- @param #AUFTRAG self
-- @param #number WeaponType Weapon type. Default is `ENUMS.WeaponFlag.Auto`.
@ -1537,11 +1548,11 @@ function AUFTRAG:SetMissionSpeed(Speed)
return self
end
--- Set max engage range.
--- Set max mission range. Only applies if the AUFTRAG is handled by an AIRWING or CHIEF. This is the max allowed distance from the airbase to the target.
-- @param #AUFTRAG self
-- @param #number Range Max range in NM. Default 100 NM.
-- @return #AUFTRAG self
function AUFTRAG:SetEngageRange(Range)
function AUFTRAG:SetMissionRange(Range)
self.engageRange=UTILS.NMToMeters(Range or 100)
return self
end
@ -2003,6 +2014,7 @@ function AUFTRAG:onafterStatus(From, Event, To)
self:E(self.lid..string.format("ERROR: FSM state %s != %s mission status!", fsmstate, self.status))
end
-- General info.
if self.verbose>=1 then
-- Mission start stop time.
@ -2018,6 +2030,7 @@ function AUFTRAG:onafterStatus(From, Event, To)
self:I(self.lid..string.format("Status %s: Target=%s, T=%s-%s, assets=%d, groups=%d, targets=%d, wing=%s, commander=%s", self.status, targetname, Cstart, Cstop, #self.assets, Ngroups, Ntargets, airwing, commander))
end
-- Group info.
if self.verbose>=2 then
-- Data on assigned groups.
local text="Group data:"
@ -2508,8 +2521,8 @@ function AUFTRAG:onafterCancel(From, Event, To)
self.Tover=timer.getAbsTime()
-- No more repeats.
self.missionRepeatMax=self.missionRepeated
self.NrepeatSuccess=self.missionRepeated
self.NrepeatFailure=self.repeated
self.NrepeatSuccess=self.repeated
-- Not necessary to delay the evaluaton?!
self.dTevaluate=0
@ -2556,16 +2569,16 @@ function AUFTRAG:onafterSuccess(From, Event, To)
self.status=AUFTRAG.Status.SUCCESS
self:T(self.lid..string.format("New mission status=%s", self.status))
if self.missionRepeated>=self.NrepeatSuccess then
if self.repeated>=self.NrepeatSuccess then
-- Stop mission.
self:I(self.lid..string.format("Mission SUCCESS! Number of max repeats reached [%d>=%d] ==> Stopping mission!", self.missionRepeated, self.NrepeatSuccess))
self:I(self.lid..string.format("Mission SUCCESS! Number of max repeats reached [%d>=%d] ==> Stopping mission!", self.repeated, self.NrepeatSuccess))
self:Stop()
else
-- Repeat mission.
self:I(self.lid..string.format("Mission SUCCESS! Repeating mission for the %d time (max %d times) ==> Repeat mission!", self.missionRepeated+1, self.NrepeatSuccess))
self:I(self.lid..string.format("Mission SUCCESS! Repeating mission for the %d time (max %d times) ==> Repeat mission!", self.repeated+1, self.NrepeatSuccess))
self:Repeat()
end
@ -2582,15 +2595,15 @@ function AUFTRAG:onafterFailed(From, Event, To)
self.status=AUFTRAG.Status.FAILED
self:T(self.lid..string.format("New mission status=%s", self.status))
if self.missionRepeated>=self.missionRepeatMax then
if self.repeated>=self.NrepeatFailure then
self:I(self.lid..string.format("Mission FAILED! Number of max repeats reached [%d>=%d] ==> Stopping mission!", self.missionRepeated, self.missionRepeatMax))
self:I(self.lid..string.format("Mission FAILED! Number of max repeats reached [%d>=%d] ==> Stopping mission!", self.repeated, self.NrepeatFailure))
self:Stop()
else
-- Repeat mission.
self:I(self.lid..string.format("Mission FAILED! Repeating mission for the %d time (max %d times) ==> Repeat mission!", self.missionRepeated+1, self.missionRepeatMax))
self:I(self.lid..string.format("Mission FAILED! Repeating mission for the %d time (max %d times) ==> Repeat mission!", self.repeated+1, self.NrepeatFailure))
self:Repeat()
end
@ -2611,17 +2624,22 @@ function AUFTRAG:onafterRepeat(From, Event, To)
self:T(self.lid..string.format("New mission status=%s (on Repeat)", self.status))
-- Increase repeat counter.
self.missionRepeated=self.missionRepeated+1
self.repeated=self.repeated+1
if self.wingcommander then
if self.chief then
elseif self.wingcommander then
elseif self.airwing then
-- Already at the airwing ==> Queued()
self:Queued(self.airwing)
else
self:E(self.lid.."ERROR: Mission can only be repeated by a CHIEF, WINGCOMMANDER or AIRWING! Stopping AUFTRAG")
self:Stop()
end
@ -2822,8 +2840,8 @@ end
-- @return #string Name of the target or "N/A".
function AUFTRAG:GetTargetName()
if self.engageTarget.Target then
return self.engageTarget.Name
if self.engageTarget then
return self.engageTarget:GetName()
end
return "N/A"

View File

@ -1503,7 +1503,6 @@ function FLIGHTGROUP:onafterAirborne(From, Event, To)
if self.ai then
self:_CheckGroupDone(1)
else
--if not self.ai then
self:_UpdateMenu()
end
end
@ -1706,7 +1705,7 @@ function FLIGHTGROUP:onafterUpdateRoute(From, Event, To, n)
---
if self:IsAirborne() then
self:T2(self.lid.."No waypoints left ==> CheckGroupDone")
self:I(self.lid.."No waypoints left ==> CheckGroupDone")
self:_CheckGroupDone()
end
@ -1897,7 +1896,15 @@ function FLIGHTGROUP:onafterRTB(From, Event, To, airbase, SpeedTo, SpeedHold, Sp
-- Cancel all missions.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
self:MissionCancel(mission)
local mystatus=mission:GetGroupStatus(self)
-- Check if mission is already over!
if not (mystatus==AUFTRAG.GroupStatus.DONE or mystatus==AUFTRAG.GroupStatus.CANCELLED) then
local text=string.format("Canceling mission %s in state=%s", mission.name, mission.status)
env.info(text)
self:MissionCancel(mission)
end
end
-- Defaults:
@ -1907,7 +1914,6 @@ function FLIGHTGROUP:onafterRTB(From, Event, To, airbase, SpeedTo, SpeedHold, Sp
-- Debug message.
local text=string.format("Flight group set to hold at airbase %s. SpeedTo=%d, SpeedHold=%d, SpeedLand=%d", airbase:GetName(), SpeedTo, SpeedHold, SpeedLand)
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
self:T(self.lid..text)
local althold=self.ishelo and 1000+math.random(10)*100 or math.random(4,10)*1000
@ -2087,7 +2093,6 @@ function FLIGHTGROUP:onafterWait(From, Event, To, Coord, Altitude, Speed)
-- Debug message.
local text=string.format("Flight group set to wait/orbit at altitude %d m and speed %.1f km/h", Altitude, Speed)
MESSAGE:New(text, 30, "DEBUG"):ToAllIf(self.Debug)
self:T(self.lid..text)
--TODO: set ROE passive. introduce roe event/state/variable.
@ -2111,7 +2116,6 @@ function FLIGHTGROUP:onafterRefuel(From, Event, To, Coordinate)
-- Debug message.
local text=string.format("Flight group set to refuel at the nearest tanker")
MESSAGE:New(text, 30, "DEBUG"):ToAllIf(self.Debug)
self:I(self.lid..text)
--TODO: set ROE passive. introduce roe event/state/variable.
@ -2146,7 +2150,6 @@ end
function FLIGHTGROUP:onafterRefueled(From, Event, To)
-- Debug message.
local text=string.format("Flight group finished refuelling")
MESSAGE:New(text, 30, "DEBUG"):ToAllIf(self.Debug)
self:I(self.lid..text)
-- Check if flight is done.
@ -2169,7 +2172,6 @@ function FLIGHTGROUP:onafterHolding(From, Event, To)
self.Tholding=timer.getAbsTime()
local text=string.format("Flight group %s is HOLDING now", self.groupname)
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
self:T(self.lid..text)
-- Add flight to waiting/holding queue.
@ -2280,7 +2282,6 @@ function FLIGHTGROUP:onafterFuelLow(From, Event, To)
-- Debug message.
local text=string.format("Low fuel for flight group %s", self.groupname)
MESSAGE:New(text, 30, "DEBUG"):ToAllIf(self.Debug)
self:I(self.lid..text)
-- Set switch to true.
@ -2350,7 +2351,6 @@ function FLIGHTGROUP:onafterFuelCritical(From, Event, To)
-- Debug message.
local text=string.format("Critical fuel for flight group %s", self.groupname)
MESSAGE:New(text, 30, "DEBUG"):ToAllIf(self.Debug)
self:I(self.lid..text)
-- Set switch to true.

View File

@ -410,11 +410,6 @@ function NAVYGROUP:onafterStatus(From, Event, To)
if self.detectionOn then
self:_CheckDetectedUnits()
end
-- Current heading and position of the carrier.
local hdg=self:GetHeading()
local pos=self:GetCoordinate()
local speed=self.group:GetVelocityKNOTS()
-- Update last known position, orientation, velocity.
self:_UpdatePosition()
@ -450,35 +445,40 @@ function NAVYGROUP:onafterStatus(From, Event, To)
-- Check if group got stuck.
self:_CheckStuck()
-- Get number of tasks and missions.
local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks()
local nMissions=self:CountRemainingMissison()
local intowind=self:IsSteamingIntoWind() and UTILS.SecondsToClock(self.intowind.Tstop-timer.getAbsTime(), true) or "N/A"
local turning=tostring(self:IsTurning())
local alt=pos.y
local speedExpected=UTILS.MpsToKnots(self.speed or 0)
local wpidxCurr=self.currentwp
local wpuidCurr=0
local wpidxNext=self:GetWaypointIndexNext()
local wpuidNext=0
local wpDist=UTILS.MetersToNM(self:GetDistanceToWaypoint())
local wpETA=UTILS.SecondsToClock(self:GetTimeToWaypoint(), true)
local roe=self:GetROE() or 0
local als=self:GetAlarmstate() or 0
if self.verbose>=1 then
-- Info text.
local text=string.format("%s [ROE=%d,AS=%d, T/M=%d/%d]: Wp=%d[%d]-->%d[%d] (of %d) Dist=%.1f NM ETA=%s - Speed=%.1f (%.1f) kts, Depth=%.1f m, Hdg=%03d, Turn=%s Collision=%d IntoWind=%s",
fsmstate, roe, als, nTaskTot, nMissions, wpidxCurr, wpuidCurr, wpidxNext, wpuidNext, #self.waypoints, wpDist, wpETA, speed, speedExpected, alt, hdg, turning, freepath, intowind)
self:I(self.lid..text)
-- Get number of tasks and missions.
local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks()
local nMissions=self:CountRemainingMissison()
local intowind=self:IsSteamingIntoWind() and UTILS.SecondsToClock(self.intowind.Tstop-timer.getAbsTime(), true) or "N/A"
local turning=tostring(self:IsTurning())
local alt=self.position.y
local speed=UTILS.MpsToKnots(self.velocity)
local speedExpected=UTILS.MpsToKnots(self.speed or 0)
local wpidxCurr=self.currentwp
local wpuidCurr=0
local wpidxNext=self:GetWaypointIndexNext()
local wpuidNext=0
local wpDist=UTILS.MetersToNM(self:GetDistanceToWaypoint())
local wpETA=UTILS.SecondsToClock(self:GetTimeToWaypoint(), true)
local roe=self:GetROE() or 0
local als=self:GetAlarmstate() or 0
-- Info text.
local text=string.format("%s [ROE=%d,AS=%d, T/M=%d/%d]: Wp=%d[%d]-->%d[%d] (of %d) Dist=%.1f NM ETA=%s - Speed=%.1f (%.1f) kts, Depth=%.1f m, Hdg=%03d, Turn=%s Collision=%d IntoWind=%s",
fsmstate, roe, als, nTaskTot, nMissions, wpidxCurr, wpuidCurr, wpidxNext, wpuidNext, #self.waypoints, wpDist, wpETA, speed, speedExpected, alt, self.heading, turning, freepath, intowind)
self:I(self.lid..text)
end
else
-- Info text.
local text=string.format("State %s: Alive=%s", fsmstate, tostring(self:IsAlive()))
self:I(self.lid..text)
self:T(self.lid..text)
end

View File

@ -401,7 +401,17 @@ function OPSGROUP:GetLifePoints()
end
end
--- Set default cruise speed..
--- Set verbosity level.
-- @param #OPSGROUP self
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
-- @return #OPSGROUP self
function OPSGROUP:SetVerbosity(VerbosityLevel)
self.verbose=VerbosityLevel or 0
return self
end
--- Set default cruise speed.
-- @param #OPSGROUP self
-- @param #number Speed Speed in knots.
-- @return #OPSGROUP self
@ -1017,7 +1027,7 @@ function OPSGROUP:RemoveWaypoint(wpindex)
self.passedfinalwp=true
end
env.info("FF passed final waypoint after remove! current wp = "..self.currentwp)
--env.info("FF passed final waypoint after remove! current wp = "..self.currentwp)
self:_CheckGroupDone(1)
@ -1043,8 +1053,7 @@ function OPSGROUP:RemoveWaypoint(wpindex)
self.currentwp=self.currentwp-1
end
--self.currentwp=math.max(1, self.currentwp-1)
env.info("FF current waypoint after remove "..self.currentwp)
--env.info("FF current waypoint after remove "..self.currentwp)
end
@ -1099,7 +1108,7 @@ function OPSGROUP:SetTask(DCSTask)
text=text..string.format("\n[%d] %s", i, tostring(task.id))
end
end
self:I(self.lid..text)
self:T(self.lid..text)
end
return self
@ -1123,7 +1132,7 @@ function OPSGROUP:PushTask(DCSTask)
text=text..string.format("\n[%d] %s", i, tostring(task.id))
end
end
self:I(self.lid..text)
self:T(self.lid..text)
end
return self
@ -1367,7 +1376,6 @@ function OPSGROUP:RemoveTask(Task)
-- Update route if this is a waypoint task.
if task.type==OPSGROUP.TaskType.WAYPOINT and task.status==OPSGROUP.TaskStatus.SCHEDULED then
self:_CheckGroupDone(1)
--self:__UpdateRoute(-1)
end
return true
@ -1445,7 +1453,6 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- Debug message.
local text=string.format("Task %s ID=%d execute", tostring(Task.description), Task.id)
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
self:I(self.lid..text)
-- Cancel current task if there is any.
@ -1556,7 +1563,6 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task)
-- Debug info.
local text=string.format("Current task %s ID=%d cancelled (flag %s=%d)", Task.description, Task.id, Task.stopflag:GetName(), stopflag)
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
self:I(self.lid..text)
-- Set stop flag. When the flag is true, the _TaskDone function is executed and calls :TaskDone()
@ -1617,7 +1623,6 @@ function OPSGROUP:onafterTaskDone(From, Event, To, Task)
-- Debug message.
local text=string.format("Task done: %s ID=%d", Task.description, Task.id)
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
self:I(self.lid..text)
-- No current task.
@ -1979,7 +1984,11 @@ end
function OPSGROUP:onafterMissionCancel(From, Event, To, Mission)
if self.currentmission and Mission.auftragsnummer==self.currentmission then
---
-- Current Mission
---
-- Get mission waypoint task.
local Task=Mission:GetGroupWaypointTask(self)
@ -1995,11 +2004,15 @@ function OPSGROUP:onafterMissionCancel(From, Event, To, Mission)
else
-- Not the current mission.
-- TODO: remove mission from queue?
---
-- NOT the current mission
---
-- Set mission group status.
Mission:SetGroupStatus(self, AUFTRAG.GroupStatus.CANCELLED)
Mission:SetGroupStatus(self, AUFTRAG.GroupStatus.CANCELLED)
-- Remove mission from queue
self:RemoveMission(Mission)
-- Send group RTB or WAIT if nothing left to do.
self:_CheckGroupDone(1)
@ -2019,7 +2032,6 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission)
-- Debug info.
local text=string.format("Mission %s DONE!", Mission.name)
self:I(self.lid..text)
MESSAGE:New(text, 30, self.groupname):ToAllIf(self.Debug)
-- Set group status.
Mission:SetGroupStatus(self, AUFTRAG.GroupStatus.DONE)
@ -2241,7 +2253,6 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
local text=string.format("Group passed waypoint %s/%d ID=%d: final=%s detour=%s astar=%s",
tostring(wpindex), #self.waypoints, Waypoint.uid, tostring(self.passedfinalwp), tostring(Waypoint.detour), tostring(Waypoint.astar))
self:I(self.lid..text)
MESSAGE:New(text, 30, "DEBUG"):ToAllIf(self.Debug)
end
@ -2623,12 +2634,12 @@ end
-- @param #OPSGROUP self
function OPSGROUP:_PrintTaskAndMissionStatus()
---
-- Tasks: verbose >= 2
---
-- Tasks: verbose >= 3
---
-- Task queue.
if #self.taskqueue>0 and self.verbose>=2 then
if self.verbose>=3 and #self.taskqueue>0 then
local text=string.format("Tasks #%d", #self.taskqueue)
for i,_task in pairs(self.taskqueue) do
local task=_task --Ops.OpsGroup#OPSGROUP.Task
@ -2660,11 +2671,11 @@ function OPSGROUP:_PrintTaskAndMissionStatus()
end
---
-- Missions: verbose>=1
-- Missions: verbose>=2
---
-- Current mission name.
if self.verbose>0 then
if self.verbose>=2 then
local Mission=self:GetMissionByID(self.currentmission)
-- Current status.
@ -2687,7 +2698,8 @@ end
--- Enhance waypoint table.
-- @param #OPSGROUP self
-- @return #OPSGROUP.Waypoint Waypoint data.
-- @param #OPSGROUP.Waypoint Waypoint data.
-- @return #OPSGROUP.Waypoint Modified waypoint data.
function OPSGROUP:_CreateWaypoint(waypoint)
-- Set uid.

View File

@ -136,15 +136,20 @@ function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName)
-- Defaults.
self.Ngroups=Ngroups or 3
self:SetEngagementRange()
self:SetMissionRange()
self:SetSkill(AI.Skill.GOOD)
self:SetVerbosity(0)
-- Everyone can ORBIT.
self:AddMissionCapability(AUFTRAG.Type.ORBIT)
-- Generalized attribute.
self.attribute=self.templategroup:GetAttribute()
-- Aircraft type.
self.aircrafttype=self.templategroup:GetTypeName()
-- Refueling system.
self.refuelSystem=select(2, self.templategroup:GetUnit(1):IsRefuelable())
self.tankerSystem=select(2, self.templategroup:GetUnit(1):IsTanker())
@ -201,8 +206,6 @@ function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName)
BASE:TraceClass(self.ClassName)
BASE:TraceLevel(1)
end
self.Debug=true
return self
end
@ -241,10 +244,19 @@ function SQUADRON:SetSkill(Skill)
return self
end
--- Set verbosity level.
-- @param #SQUADRON self
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
-- @return #SQUADRON self
function SQUADRON:SetVerbosity(VerbosityLevel)
self.verbose=VerbosityLevel or 0
return self
end
--- Set turnover and repair time. If an asset returns from a mission to the airwing, it will need some time until the asset is available for further missions.
-- @param #SQUADRON self
-- @param #number MaintenanceTime Time in minutes it takes until a flight is combat ready again. Default is 0 min.
-- @param #number RepairTime Time in minutes it takes to repair a flight for each percent damage taken. Default is 0 min.
-- @param #number RepairTime Time in minutes it takes to repair a flight for each life point taken. Default is 0 min.
-- @return #SQUADRON self
function SQUADRON:SetTurnoverTime(MaintenanceTime, RepairTime)
self.maintenancetime=MaintenanceTime and MaintenanceTime*60 or 0
@ -350,12 +362,12 @@ function SQUADRON:GetMissionPeformance(MissionType)
return -1
end
--- Set max engagement range.
--- Set max mission range. Only missions in a circle of this radius around the squadron airbase are executed.
-- @param #SQUADRON self
-- @param #number EngageRange Engagement range in NM. Default 80 NM.
-- @param #number Range Range in NM. Default 100 NM.
-- @return #SQUADRON self
function SQUADRON:SetEngagementRange(EngageRange)
self.engageRange=UTILS.NMToMeters(EngageRange or 80)
function SQUADRON:SetMissionRange(Range)
self.engageRange=UTILS.NMToMeters(Range or 100)
return self
end
@ -565,27 +577,31 @@ end
-- @param #string To To state.
function SQUADRON:onafterStatus(From, Event, To)
-- FSM state.
local fsmstate=self:GetState()
if self.verbose>=1 then
local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName) or "N/A"
local modex=self.modex and self.modex or -1
local skill=self.skill and tostring(self.skill) or "N/A"
-- FSM state.
local fsmstate=self:GetState()
local NassetsTot=#self.assets
local NassetsInS=self:CountAssetsInStock()
local NassetsQP=0 ; local NassetsP=0 ; local NassetsQ=0
if self.airwing then
NassetsQP, NassetsP, NassetsQ=self.airwing:CountAssetsOnMission(nil, self)
end
-- Short info.
local text=string.format("%s [Type=%s, Callsign=%s, Modex=%d, Skill=%s]: Assets Total=%d, InStock=%d, OnMission=%d [P=%d, Q=%d]",
fsmstate, self.aircrafttype, callsign, modex, skill, NassetsTot, NassetsInS, NassetsQP, NassetsP, NassetsQ)
self:I(self.lid..text)
-- Check if group has detected any units.
self:_CheckAssetStatus()
local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName) or "N/A"
local modex=self.modex and self.modex or -1
local skill=self.skill and tostring(self.skill) or "N/A"
local NassetsTot=#self.assets
local NassetsInS=self:CountAssetsInStock()
local NassetsQP=0 ; local NassetsP=0 ; local NassetsQ=0
if self.airwing then
NassetsQP, NassetsP, NassetsQ=self.airwing:CountAssetsOnMission(nil, self)
end
-- Short info.
local text=string.format("%s [Type=%s, Call=%s, Modex=%d, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]",
fsmstate, self.aircrafttype, callsign, modex, skill, NassetsTot, NassetsInS, NassetsQP, NassetsP, NassetsQ)
self:I(self.lid..text)
-- Check if group has detected any units.
self:_CheckAssetStatus()
end
if not self:IsStopped() then
self:__Status(-30)
@ -597,7 +613,8 @@ end
-- @param #SQUADRON self
function SQUADRON:_CheckAssetStatus()
if self.verbose>=0 then
if self.verbose>=2 then
local text=""
for j,_asset in pairs(self.assets) do
local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset
@ -647,8 +664,7 @@ function SQUADRON:_CheckAssetStatus()
-- Payload info.
local payload=asset.payload and table.concat(self.airwing:GetPayloadMissionTypes(asset.payload), ", ") or "None"
text=text..", Payload={"..payload.."}"
else
---

View File

@ -136,9 +136,12 @@ function TARGET:New(TargetObject)
self:AddObject(TargetObject)
local Target=self.targets[1] --#TARGET.Object
self.name=self:GetTargetName(Target)
self.category=self:GetTargetCategory(Target)
-- Log ID.
self.lid=string.format("TARGET #%03d %s | ", _TARGETID, self.category)
-- Start state.
@ -660,6 +663,56 @@ function TARGET:GetTargetCoordinate(Target)
return nil
end
--- Get target name.
-- @param #TARGET self
-- @param #TARGET.Object Target Target object.
-- @return #string Name of the target object.
function TARGET:GetTargetName(Target)
if Target.Type==TARGET.ObjectType.GROUP then
if Target.Object and Target.Object:IsAlive() then
return Target.Object:GetName()
end
elseif Target.Type==TARGET.ObjectType.UNIT then
if Target.Object and Target.Object:IsAlive() then
return Target.Object:GetName()
end
elseif Target.Type==TARGET.ObjectType.STATIC then
if Target.Object and Target.Object:IsAlive() then
return Target.Object:GetName()
end
elseif Target.Type==TARGET.ObjectType.AIRBASE then
if Target.Status==TARGET.ObjectStatus.ALIVE then
return Target.Object:GetName()
end
elseif Target.Type==TARGET.ObjectType.COORDINATE then
local coord=Target.Object --Core.Point#COORDINATE
return coord:ToStringMGRS()
end
return "Unknown"
end
--- Get name.
-- @param #TARGET self
-- @return #string Name of the target usually the first object.
function TARGET:GetName()
return self.name
end
--- Get coordinate.
-- @param #TARGET self
-- @return Core.Point#COORDINATE Coordinate of the target.

View File

@ -1,6 +1,6 @@
--- **Utils** - Lua Profiler.
--
--
-- Find out how many times functions are called and how much real time it costs.
--
-- ===
--
@ -13,11 +13,17 @@
--- PROFILER class.
-- @type PROFILER
-- @field #string ClassName Name of the class.
-- @field #table Counters Counters.
-- @field #table Counters Function counters.
-- @field #table dInfo Info.
-- @field #table fTime Function time.
-- @field #table fTimeTotal Total function time.
-- @field #table eventhandler Event handler to get mission end event.
-- @field #number TstartGame Game start time timer.getTime().
-- @field #number TstartOS OS real start time os.clock.
-- @field #boolean logUnknown Log unknown functions. Default is off.
-- @field #number lowCpsThres Low calls per second threashold. Only write output if function has more calls per second than this value.
-- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler".
-- @field #string fileNameSuffix Output file name prefix, e.g. "txt"
--- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? *
--
@ -27,8 +33,8 @@
--
-- # The PROFILER Concept
--
-- Profile your lua code. This tells you, which functions are called very often and which consume most CPU time.
-- With this information you could optimize the perfomance of your code.
-- Profile your lua code. This tells you, which functions are called very often and which consume most real time.
-- With this information you can optimize the perfomance of your code.
--
-- # Prerequisites
--
@ -37,19 +43,22 @@
--
-- # Start
--
-- The profiler can simply be started by
-- The profiler can simply be started with the @{#PROFILER.Start}(*Delay, Duration*) function
--
-- PROFILER.Start()
--
-- The start can be delayed by specifying a the amount of seconds as argument, e.g. PROFILER.Start(60) to start profiling in 60 seconds.
-- The optional parameter *Delay* can be used to delay the start by a certain amount of seconds and the optional parameter *Duration* can be used to
-- stop the profiler after a certain amount of seconds.
--
-- # Stop
--
-- The profiler automatically stops when the mission ends. But it can be stopped any time by calling
-- The profiler automatically stops when the mission ends. But it can be stopped any time with the @{#PROFILER.Stop}(*Delay*) function
--
-- PROFILER.Stop()
--
-- The stop call can be delayed by specifying the delay in seconds as optional argument, e.g. PROFILER.Stop(120) to stop it in 120 seconds.
-- The optional parameter *Delay* can be used to specify a delay after which the profiler is stopped.
--
-- When the profiler is stopped, the output is written to a file.
--
-- # Output
--
@ -57,13 +66,19 @@
--
-- X:\User\<Your User Name>\Saved Games\DCS OpenBeta\Logs
--
-- ## Sort Output
-- The default file name is "MooseProfiler.txt". If that file exists, the file name is "MooseProfiler-001.txt" etc.
--
-- By default the output is sorted with respect to the total time a function used.
-- ## Data
--
-- The output can also be sorted with respect to the number of times the function was called by setting
-- The data in the output file provides information on the functions that were called in the mission.
--
-- PROFILER.sortBy=1
-- It will tell you how many times a function was called in total, how many times per second, how much time in total and the percentage of time.
--
-- If you only want output for functions that are called more than X times per second, you can set
--
-- PROFILER.lowCpsThres=1.5
--
-- With this setting, only functions which are called more than 1.5 times per second are displayed.
--
-- @field #PROFILER
PROFILER = {
@ -73,11 +88,10 @@ PROFILER = {
fTime = {},
fTimeTotal = {},
eventHandler = {},
startTime = nil,
runTime = nil,
logUnknown = false,
lowCpsThres = 5,
fileName = "",
lowCpsThres = 0.0,
fileNamePrefix = "MooseProfiler",
fileNameSuffix = "txt"
}
--- Waypoint data.
@ -88,10 +102,6 @@ PROFILER = {
-- @field #number count Number of function calls.
-- @field #number tm Total time in seconds.
PROFILER.logUnknown=false -- Log unknown functions
PROFILER.lowCpsThres=0 -- Skip results with less than X calls per second
PROFILER.fileName="_LuaProfiler.txt"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start/Stop Profiler
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -104,28 +114,58 @@ function PROFILER.Start(Delay, Duration)
if Delay and Delay>0 then
BASE:ScheduleOnce(Delay, PROFILER.Start, 0, Duration)
else
-- Check if os and lfs are available.
local go=true
if not os then
error("Profiler needs os to be desanitized")
go=false
end
if not lfs then
error("Profiler needs lfs to be desanitized")
go=false
end
if not go then
return
end
-- Set start time.
PROFILER.TstartGame=timer.getTime()
PROFILER.TstartOS=os.clock()
-- Add event handler.
world.addEventHandler(PROFILER.eventHandler)
--[[
-- Message to screen.
local function showProfilerRunning()
timer.scheduleFunction(showProfilerRunning, nil, timer.getTime()+600)
trigger.action.outText("### Profiler running ###", 600)
end
end
-- Message.
showProfilerRunning()
]]
-- Info in log.
BASE:I('############################ Profiler Started ############################')
env.info('############################ Profiler Started ############################')
if Duration then
env.info(string.format("Duration %d seconds", Duration))
else
env.info(string.format("Stopped when mission ends"))
end
env.info(string.format("Calls per second threshold %.3f/sec", PROFILER.lowCpsThres))
env.info(string.format("Log file \"%s.%s\"", PROFILER.fileNamePrefix, PROFILER.fileNameSuffix))
env.info('###############################################################################')
-- Message on screen
local duration=Duration or 600
trigger.action.outText("### Profiler running ###", duration)
-- Set hook.
debug.sethook(PROFILER.hook, "cr")
-- Auto stop profiler.
if Duration then
PROFILER.Stop(Duration)
end
@ -148,12 +188,14 @@ function PROFILER.Stop(Delay)
debug.sethook()
-- Run time.
PROFILER.runTimeGame=timer.getTime()-PROFILER.TstartGame
PROFILER.runTimeOS=os.clock()-PROFILER.TstartOS
-- Run time game.
local runTimeGame=timer.getTime()-PROFILER.TstartGame
-- Run time real OS.
local runTimeOS=os.clock()-PROFILER.TstartOS
-- Show info.
PROFILER.showInfo()
PROFILER.showInfo(runTimeGame, runTimeOS)
end
@ -237,20 +279,20 @@ end
--- Show table.
-- @param #table data Data table.
-- @param #function f The file.
-- @param #boolean detailed Show detailed info.
function PROFILER.showTable(data, f, detailed)
-- @param #number runTimeGame Game run time in seconds.
function PROFILER.showTable(data, f, runTimeGame)
-- Loop over data.
for i=1, #data do
local t=data[i] --#PROFILER.Data
-- Calls per second.
local cps=t.count/PROFILER.runTimeGame
local cps=t.count/runTimeGame
if (cps>=PROFILER.lowCpsThres) then
-- Output
local text=string.format("%30s: %8d calls %8.1f/sec - Time %8.3f sec (%.3f %%) %s line %s", t.func, t.count, cps, t.tm, t.tm/PROFILER.runTimeGame*100, tostring(t.src), tostring(t.line))
local text=string.format("%30s: %8d calls %8.1f/sec - Time %8.3f sec (%.3f %%) %s line %s", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, tostring(t.src), tostring(t.line))
PROFILER._flog(f, text)
end
@ -259,16 +301,50 @@ function PROFILER.showTable(data, f, detailed)
end
--- Write info to output file.
function PROFILER.showInfo()
-- @return #string File name.
function PROFILER.getfilename()
local dir=lfs.writedir()..[[Logs\]]
local file=dir..PROFILER.fileNamePrefix.."."..PROFILER.fileNameSuffix
if not UTILS.FileExists(file) then
return file
end
for i=1,999 do
local file=string.format("%s%s-%03d.%s", dir,PROFILER.fileNamePrefix, i, PROFILER.fileNameSuffix)
if not UTILS.FileExists(file) then
return file
end
end
end
--- Write info to output file.
-- @param #number runTimeGame Game time in seconds.
-- @param #number runTimeOS OS time in seconds.
function PROFILER.showInfo(runTimeGame, runTimeOS)
-- Output file.
local file=lfs.writedir()..[[Logs\]]..PROFILER.fileName
local file=PROFILER.getfilename()
local f=io.open(file, 'w')
-- Gather data.
local Ttot=0
local Calls=0
local t={}
local tcopy=nil --#PROFILER.Data
local tserialize=nil --#PROFILER.Data
local tforgen=nil --#PROFILER.Data
local tpairs=nil --#PROFILER.Data
for func, count in pairs(PROFILER.Counters) do
local s,src,line,tm=PROFILER.getData(func)
@ -277,26 +353,80 @@ function PROFILER.showInfo()
if s==nil then s="<Unknown>" end
end
if s~=nil and s~="_copy" and s~="_Serialize" and s~="(for generator)" and s~="pairs" then
t[#t+1]=
if s~=nil then
-- Profile data.
local T=
{ func=s,
src=src,
line=line,
count=count,
tm=tm,
}
} --#PROFILER.Data
-- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data.
if s=="_copy" then
if tcopy==nil then
tcopy=T
else
tcopy.count=tcopy.count+T.count
tcopy.tm=tcopy.tm+T.tm
end
elseif s=="_Serialize" then
if tserialize==nil then
tserialize=T
else
tserialize.count=tserialize.count+T.count
tserialize.tm=tserialize.tm+T.tm
end
elseif s=="(for generator)" then
if tforgen==nil then
tforgen=T
else
tforgen.count=tforgen.count+T.count
tforgen.tm=tforgen.tm+T.tm
end
elseif s=="pairs" then
if tpairs==nil then
tpairs=T
else
tpairs.count=tpairs.count+T.count
tpairs.tm=tpairs.tm+T.tm
end
else
table.insert(t, T)
end
-- Total function time.
Ttot=Ttot+tm
-- Total number of calls.
Calls=Calls+count
end
end
-- Add special cases.
if tcopy then
table.insert(t, tcopy)
end
if tserialize then
table.insert(t, tserialize)
end
if tforgen then
table.insert(t, tforgen)
end
if tpairs then
table.insert(t, tpairs)
end
env.info("**************************************************************************************************")
env.info(string.format("Profiler"))
env.info(string.format("--------"))
env.info(string.format("* Runtime Game : %s = %d sec", UTILS.SecondsToClock(PROFILER.runTimeGame, true), PROFILER.runTimeGame))
env.info(string.format("* Runtime Real : %s = %d sec", UTILS.SecondsToClock(PROFILER.runTimeOS, true), PROFILER.runTimeOS))
env.info(string.format("* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/PROFILER.runTimeGame*100))
env.info(string.format("* Runtime Game : %s = %d sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame))
env.info(string.format("* Runtime Real : %s = %d sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS))
env.info(string.format("* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100))
env.info(string.format("* Total functions : %d", #t))
env.info(string.format("* Total func calls : %d", Calls))
env.info(string.format("* Writing to file : \"%s\"", file))
@ -315,16 +445,16 @@ function PROFILER.showInfo()
PROFILER._flog(f,"---- Profiler Report ----")
PROFILER._flog(f,"-------------------------")
PROFILER._flog(f,"")
PROFILER._flog(f,string.format("* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock(PROFILER.runTimeGame, true), PROFILER.runTimeGame))
PROFILER._flog(f,string.format("* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock(PROFILER.runTimeOS, true), PROFILER.runTimeOS).." (can vary significantly compared to the game time)")
PROFILER._flog(f,string.format("* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/PROFILER.runTimeGame*100))
PROFILER._flog(f,string.format("* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame))
PROFILER._flog(f,string.format("* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS))
PROFILER._flog(f,string.format("* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100))
PROFILER._flog(f,"")
PROFILER._flog(f,string.format("* Total functions = %d", #t))
PROFILER._flog(f,string.format("* Total func calls = %d", Calls))
PROFILER._flog(f,"")
PROFILER._flog(f,"************************************************************************************************************************")
PROFILER._flog(f,"")
PROFILER.showTable(t, f, true)
PROFILER.showTable(t, f, runTimeGame)
-- Sort by number of calls.
table.sort(t, function(a,b) return a.count>b.count end)
@ -337,7 +467,7 @@ function PROFILER.showInfo()
PROFILER._flog(f,"---- Data Sorted by Calls ----")
PROFILER._flog(f,"------------------------------")
PROFILER._flog(f,"")
PROFILER.showTable(t, f, true)
PROFILER.showTable(t, f, runTimeGame)
-- Closing.
PROFILER._flog(f,"")

View File

@ -755,7 +755,7 @@ end
-- @param #string Text The updated text, displayed in the mark panel.
function MARKER:onafterTextUpdate(From, Event, To, Text)
self:I(self.lid..string.format("New Marker Text:\n%s", Text))
self:T(self.lid..string.format("New Marker Text:\n%s", Text))
end
@ -767,7 +767,7 @@ end
-- @param Core.Point#COORDINATE Coordinate The updated coordinates.
function MARKER:onafterCoordUpdate(From, Event, To, Coordinate)
self:I(self.lid..string.format("New Marker Coordinate in LL DMS: %s", Coordinate:ToStringLLDMS()))
self:T(self.lid..string.format("New Marker Coordinate in LL DMS: %s", Coordinate:ToStringLLDMS()))
end