diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 213805a8d..6f228c997 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -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. diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 0395508a3..e2633f33c 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -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 --- diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 698028882..59755949f 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -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" diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index fdbcae847..2b15fb846 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -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. diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index dbb1896d7..9aeec162b 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -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 diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 589891d63..a4cf182ab 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -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. diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index 8fe221598..b21ca511b 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -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 --- diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index e47770ef4..9f37e0b7f 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -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. diff --git a/Moose Development/Moose/Utilities/Profiler.lua b/Moose Development/Moose/Utilities/Profiler.lua index 1ab6368ff..a5b2d9bbf 100644 --- a/Moose Development/Moose/Utilities/Profiler.lua +++ b/Moose Development/Moose/Utilities/Profiler.lua @@ -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\\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="" 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,"") diff --git a/Moose Development/Moose/Wrapper/Marker.lua b/Moose Development/Moose/Wrapper/Marker.lua index 3cc74baec..5d65d3fbe 100644 --- a/Moose Development/Moose/Wrapper/Marker.lua +++ b/Moose Development/Moose/Wrapper/Marker.lua @@ -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