diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 313482880..827db5778 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -26,8 +26,6 @@ -- @module Core.Base -- @image Core_Base.JPG - - local _TraceOnOff = true local _TraceLevel = 1 local _TraceAll = false diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 09d447f65..a9725c636 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -71,7 +71,7 @@ -- -- -- ### Author: **FlightControl** --- ### Contributions: +-- ### Contributions: **funkyfranky** -- -- === -- @@ -81,6 +81,12 @@ do -- FSM --- @type FSM + -- @field #string ClassName Name of the class. + -- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler. + -- @field #table options Options. + -- @field #table subs Subs. + -- @field #table Scores Scores. + -- @field #string current Current state name. -- @extends Core.Base#BASE @@ -369,8 +375,7 @@ do -- FSM self._EventSchedules = {} self.CallScheduler = SCHEDULER:New( self ) - - + return self end @@ -379,7 +384,6 @@ do -- FSM -- @param #FSM self -- @param #string State A string defining the start state. function FSM:SetStartState( State ) - self._StartState = State self.current = State end @@ -389,7 +393,6 @@ do -- FSM -- @param #FSM self -- @return #string A string containing the start state. function FSM:GetStartState() - return self._StartState or {} end @@ -406,6 +409,7 @@ do -- FSM Transition.Event = Event Transition.To = To + -- Debug message. self:T2( Transition ) self._Transitions[Transition] = Transition @@ -414,9 +418,9 @@ do -- FSM --- Returns a table of the transition rules defined within the FSM. - -- @return #table - function FSM:GetTransitions() - + -- @param #FSM self + -- @return #table Transitions. + function FSM:GetTransitions() return self._Transitions or {} end @@ -448,7 +452,8 @@ do -- FSM --- Returns a table of the SubFSM rules defined within the FSM. - -- @return #table + -- @param #FSM self + -- @return #table Sub processes. function FSM:GetProcesses() self:F( { Processes = self._Processes } ) @@ -480,15 +485,17 @@ do -- FSM end --- Adds an End state. - function FSM:AddEndState( State ) - + -- @param #FSM self + -- @param #string State The FSM state. + function FSM:AddEndState( State ) self._EndStates[State] = State self.endstates[State] = State end --- Returns the End states. - function FSM:GetEndStates() - + -- @param #FSM self + -- @return #table End states. + function FSM:GetEndStates() return self._EndStates or {} end @@ -532,18 +539,22 @@ do -- FSM end --- Returns a table with the scores defined. - function FSM:GetScores() - + -- @param #FSM self + -- @param #table Scores. + function FSM:GetScores() return self._Scores or {} end --- Returns a table with the Subs defined. - function FSM:GetSubs() - + -- @param #FSM self + -- @return #table Sub processes. + function FSM:GetSubs() return self.options.subs end - + --- Load call backs. + -- @param #FSM self + -- @param #table CallBackTable Table of call backs. function FSM:LoadCallBacks( CallBackTable ) for name, callback in pairs( CallBackTable or {} ) do @@ -551,21 +562,34 @@ do -- FSM end end - + + --- Event map. + -- @param #FSM self + -- @param #table Events Events. + -- @param #table EventStructure Event structure. function FSM:_eventmap( Events, EventStructure ) local Event = EventStructure.Event local __Event = "__" .. EventStructure.Event + self[Event] = self[Event] or self:_create_transition(Event) self[__Event] = self[__Event] or self:_delayed_transition(Event) + + -- Debug message. self:T2( "Added methods: " .. Event .. ", " .. __Event ) + Events[Event] = self.Events[Event] or { map = {} } self:_add_to_map( Events[Event].map, EventStructure ) end - + + --- Sub maps. + -- @param #FSM self + -- @param #table subs Subs. + -- @param #table sub Sub. + -- @param #string name Name. function FSM:_submap( subs, sub, name ) - --self:F( { sub = sub, name = name } ) + subs[sub.From] = subs[sub.From] or {} subs[sub.From][sub.Event] = subs[sub.From][sub.Event] or {} @@ -578,22 +602,23 @@ do -- FSM subs[sub.From][sub.Event][sub].ReturnEvents = sub.ReturnEvents or {} -- these events need to be given to find the correct continue event ... if none given, the processing will stop. subs[sub.From][sub.Event][sub].name = name subs[sub.From][sub.Event][sub].fsmparent = self + end - + --- Call handler. + -- @param #FSM self + -- @param #string step Step "onafter", "onbefore", "onenter", "onleave". + -- @param #string trigger Trigger. + -- @param #table params Parameters. + -- @param #string EventName Event name. + -- @return Value. function FSM:_call_handler( step, trigger, params, EventName ) + --env.info(string.format("FF T=%.3f _call_handler step=%s, trigger=%s, event=%s", timer.getTime(), step, trigger, EventName)) local handler = step .. trigger - local ErrorHandler = function( errmsg ) - - env.info( "Error in SCHEDULER function:" .. errmsg ) - if BASE.Debug ~= nil then - env.info( BASE.Debug.traceback() ) - end - - return errmsg - end + if self[handler] then + if step == "onafter" or step == "OnAfter" then self:T( ":::>" .. step .. params[2] .. " : " .. params[1] .. " >> " .. params[2] .. ">" .. step .. params[2] .. "()" .. " >> " .. params[3] ) elseif step == "onbefore" or step == "OnBefore" then @@ -604,14 +629,31 @@ do -- FSM self:T( ":::>" .. step .. params[1] .. " : " .. params[1] .. ">" .. step .. params[1] .. "()" .. " >> " .. params[2] .. " >> " .. params[3] ) else self:T( ":::>" .. step .. " : " .. params[1] .. " >> " .. params[2] .. " >> " .. params[3] ) - end + end + self._EventSchedules[EventName] = nil + + -- Error handler. + local ErrorHandler = function( errmsg ) + env.info( "Error in SCHEDULER function:" .. errmsg ) + if BASE.Debug ~= nil then + env.info( BASE.Debug.traceback() ) + end + return errmsg + end + + -- Protected call. local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) + return Value end + end - --- @param #FSM self + --- Handler. + -- @param #FSM self + -- @param #string EventName Event name. + -- @param ... Arguments. function FSM._handler( self, EventName, ... ) local Can, To = self:can( EventName ) @@ -621,7 +663,11 @@ do -- FSM end if Can then + + -- From state. local From = self.current + + -- Parameters. local Params = { From, EventName, To, ... } @@ -632,8 +678,8 @@ do -- FSM self["onafter".. EventName] or self["OnAfter".. EventName] or self["onenter".. To] or - self["OnEnter".. To] - then + self["OnEnter".. To] then + if self:_call_handler( "onbefore", EventName, Params, EventName ) == false then self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** onbefore" .. EventName ) return false @@ -653,8 +699,11 @@ do -- FSM end end end + else + local ClassName = self:GetClassName() + if ClassName == "FSM" then self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To ) end @@ -672,84 +721,125 @@ do -- FSM end end + -- New current state. self.current = To local execute = true local subtable = self:_gosub( From, EventName ) + for _, sub in pairs( subtable ) do + --if sub.nextevent then -- self:F2( "nextevent = " .. sub.nextevent ) -- self[sub.nextevent]( self ) --end + self:T( "*** FSM *** Sub *** " .. sub.StartEvent ) + sub.fsm.fsmparent = self sub.fsm.ReturnEvents = sub.ReturnEvents sub.fsm[sub.StartEvent]( sub.fsm ) + execute = false end local fsmparent, Event = self:_isendstate( To ) + if fsmparent and Event then + self:T( "*** FSM *** End *** " .. Event ) + self:_call_handler("onenter", To, Params, EventName ) self:_call_handler("OnEnter", To, Params, EventName ) self:_call_handler("onafter", EventName, Params, EventName ) self:_call_handler("OnAfter", EventName, Params, EventName ) self:_call_handler("onstate", "change", Params, EventName ) + fsmparent[Event]( fsmparent ) + execute = false end if execute then - self:_call_handler("onafter", EventName, Params, EventName ) - self:_call_handler("OnAfter", EventName, Params, EventName ) - -- only execute the call if the From state is not equal to the To state! Otherwise this function should never execute! - --if from ~= to then - self:_call_handler("onenter", To, Params, EventName ) - self:_call_handler("OnEnter", To, Params, EventName ) - --end + self:_call_handler("onafter", EventName, Params, EventName ) + self:_call_handler("OnAfter", EventName, Params, EventName ) + + self:_call_handler("onenter", To, Params, EventName ) + self:_call_handler("OnEnter", To, Params, EventName ) self:_call_handler("onstate", "change", Params, EventName ) + end else - self:T( "*** FSM *** NO Transition *** " .. self.current .. " --> " .. EventName .. " --> ? " ) + self:I( "*** FSM *** NO Transition *** " .. self.current .. " --> " .. EventName .. " --> ? " ) end return nil end - + + --- Delayed transition. + -- @param #FSM self + -- @param #string EventName Event name. + -- @return #function Function. function FSM:_delayed_transition( EventName ) + return function( self, DelaySeconds, ... ) + + -- Debug. self:T2( "Delayed Event: " .. EventName ) + local CallID = 0 if DelaySeconds ~= nil then + if DelaySeconds < 0 then -- Only call the event ONCE! + DelaySeconds = math.abs( DelaySeconds ) - if not self._EventSchedules[EventName] then + + if not self._EventSchedules[EventName] then + + -- Call _handler. CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true ) + + -- Set call ID. self._EventSchedules[EventName] = CallID + + -- Debug output. self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID))) else self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec CANCELLED as we already have such an event in the queue.", EventName, DelaySeconds)) -- reschedule end else + CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true ) + self:T2(string.format("Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID))) end else error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." ) end + + -- Debug. self:T2( { CallID = CallID } ) end + end - + + --- Create transition. + -- @param #FSM self + -- @param #string EventName Event name. + -- @return #function Function. function FSM:_create_transition( EventName ) return function( self, ... ) return self._handler( self, EventName , ... ) end end - + + --- Go sub. + -- @param #FSM self + -- @param #string ParentFrom Parent from state. + -- @param #string ParentEvent Parent event name. + -- @return #table Subs. function FSM:_gosub( ParentFrom, ParentEvent ) local fsmtable = {} if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then @@ -759,9 +849,15 @@ do -- FSM return {} end end - + + --- Is end state. + -- @param #FSM self + -- @param #string Current Current state name. + -- @return #table FSM parent. + -- @return #string Event name. function FSM:_isendstate( Current ) local FSMParent = self.fsmparent + if FSMParent and self.endstates[Current] then --self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } ) FSMParent.current = Current @@ -778,9 +874,14 @@ do -- FSM return nil end - + + --- Add to map. + -- @param #FSM self + -- @param #table Map Map. + -- @param #table Event Event table. function FSM:_add_to_map( Map, Event ) self:F3( { Map, Event } ) + if type(Event.From) == 'string' then Map[Event.From] = Event.To else @@ -788,33 +889,59 @@ do -- FSM Map[From] = Event.To end end + self:T3( { Map, Event } ) end - + + --- Get current state. + -- @param #FSM self + -- @return #string Current FSM state. function FSM:GetState() return self.current end - + + --- Get current state. + -- @param #FSM self + -- @return #string Current FSM state. function FSM:GetCurrentState() return self.current end - + --- Check if FSM is in state. + -- @param #FSM self + -- @param #string State State name. + -- @param #boolean If true, FSM is in this state. function FSM:Is( State ) return self.current == State end - + + --- Check if FSM is in state. + -- @param #FSM self + -- @param #string State State name. + -- @param #boolean If true, FSM is in this state. function FSM:is(state) return self.current == state end - + + --- Check if can do an event. + -- @param #FSM self + -- @param #string e Event name. + -- @return #boolean If true, FSM can do the event. + -- @return #string To state. function FSM:can(e) local Event = self.Events[e] + self:F3( { self.current, Event } ) + local To = Event and Event.map[self.current] or Event.map['*'] + return To ~= nil, To end - + + --- Check if cannot do an event. + -- @param #FSM self + -- @param #string e Event name. + -- @return #boolean If true, FSM cannot do the event. function FSM:cannot(e) return not self:can(e) end diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 40e65adad..0395508a3 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -1988,6 +1988,7 @@ function AIRWING:CountAssetsOnMission(MissionTypes, Squadron) end end + env.info(string.format("FF N=%d Np=%d, Nq=%d", Np+Nq, Np, Nq)) return Np+Nq, Np, Nq end diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index f808174ec..698028882 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -486,6 +486,7 @@ function AUFTRAG:New(Type) self.engageAsGroup=true self.missionRepeated=0 self.missionRepeatMax=0 + self.NrepeatSuccess=0 self.nassets=1 self.dTevaluate=0 self.Ncasualties=0 @@ -982,6 +983,10 @@ function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude) if type(Airdrome)=="string" then Airdrome=AIRBASE:FindByName(Airdrome) end + + if Airdrome:IsInstanceOf("AIRBASE") then + + end local mission=AUFTRAG:New(AUFTRAG.Type.BOMBRUNWAY) @@ -995,7 +1000,7 @@ function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude) -- Mission options: mission.missionTask=ENUMS.MissionTask.RUNWAYATTACK mission.missionAltitude=mission.engageAltitude*0.8 - mission.missionFraction=0.2 + mission.missionFraction=0.75 mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.PassiveDefense @@ -3052,7 +3057,7 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) -- BOMBRUNWAY Mission -- ------------------------ - local DCStask=CONTROLLABLE.TaskBombingRunway(nil, self.engageTarget.Target, self.engageWeaponType, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAsGroup) + local DCStask=CONTROLLABLE.TaskBombingRunway(nil, self.engageTarget:GetObject(), self.engageWeaponType, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAsGroup) table.insert(DCStasks, DCStask) @@ -3092,7 +3097,7 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) -- ESCORT Mission -- -------------------- - local DCStask=CONTROLLABLE.TaskEscort(nil, self.engageTarget.Target, self.escortVec3, LastWaypointIndex, self.engageMaxDistance, self.engageTargetTypes) + local DCStask=CONTROLLABLE.TaskEscort(nil, self.engageTarget:GetObject(), self.escortVec3, LastWaypointIndex, self.engageMaxDistance, self.engageTargetTypes) table.insert(DCStasks, DCStask) @@ -3102,7 +3107,7 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) -- FAC Mission -- ----------------- - local DCStask=CONTROLLABLE.TaskFAC_AttackGroup(nil, self.engageTarget.Target, self.engageWeaponType, self.facDesignation, self.facDatalink, self.facFreq, self.facModu, CallsignName, CallsignNumber) + local DCStask=CONTROLLABLE.TaskFAC_AttackGroup(nil, self.engageTarget:GetObject(), self.engageWeaponType, self.facDesignation, self.facDatalink, self.facFreq, self.facModu, CallsignName, CallsignNumber) table.insert(DCStasks, DCStask) diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 9b4d64818..dbb1896d7 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -416,6 +416,9 @@ function NAVYGROUP:onafterStatus(From, Event, To) local pos=self:GetCoordinate() local speed=self.group:GetVelocityKNOTS() + -- Update last known position, orientation, velocity. + self:_UpdatePosition() + -- Check if group started or stopped turning. self:_CheckTurning() @@ -440,9 +443,6 @@ function NAVYGROUP:onafterStatus(From, Event, To) end - -- Check water is ahead. - --collision=self:_CheckCollisionCoord(pos:Translate(freepath+100, hdg)) - end -- Check into wind queue. @@ -1348,10 +1348,10 @@ function NAVYGROUP:_CheckTurning() if unit and unit:IsAlive() then -- Current orientation of carrier. - local vNew=unit:GetOrientationX() + local vNew=self.orientX --unit:GetOrientationX() -- Last orientation from 30 seconds ago. - local vLast=self.Corientlast or vNew + local vLast=self.orientXLast --self.Corientlast or vNew -- We only need the X-Z plane. vNew.y=0 ; vLast.y=0 @@ -1360,7 +1360,7 @@ function NAVYGROUP:_CheckTurning() local deltaLast=math.deg(math.acos(UTILS.VecDot(vNew,vLast)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vLast))) -- Last orientation becomes new orientation - self.Corientlast=vNew + --self.Corientlast=vNew -- Carrier is turning when its heading changed by at least two degrees since last check. local turning=math.abs(deltaLast)>=2 diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index a41229802..589891d63 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -2386,7 +2386,7 @@ function OPSGROUP:onafterCheckZone(From, Event, To) end if not self:IsStopped() then - self:__CheckZone(-1) + self:__CheckZone(-10) end end @@ -2443,7 +2443,6 @@ function OPSGROUP:_CheckInZones() self:EnterZone(enterzone) end - end end @@ -3494,6 +3493,30 @@ end -- Element and Group Status Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Check if all elements of the group have the same status (or are dead). +-- @param #OPSGROUP self +-- @param #string unitname Name of unit. +function OPSGROUP:_UpdatePosition() + + if self:IsAlive() then + + self.positionLast=self.position or self:GetCoordinate() + self.headingLast=self.heading or self:GetHeading() + self.orientXLast=self.orientX or self.group:GetUnit(1):GetOrientationX() + self.velocityLast=self.velocity or self.group:GetVelocityMPS() + + self.position=self:GetCoordinate() + self.heading=self:GetHeading() + self.orientX=self.group:GetUnit(1):GetOrientationX() + self.velocity=self.group:GetVelocityMPS() + + self.dTpositionUpdate=self.TpositionUpdate and self.TpositionUpdate-timer.getAbsTime() or 0 + self.TpositionUpdate=timer.getAbsTime() + + end + +end + --- Check if all elements of the group have the same status (or are dead). -- @param #OPSGROUP self -- @param #string unitname Name of unit. diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index 3a07839fe..8fe221598 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -574,8 +574,11 @@ function SQUADRON:onafterStatus(From, Event, To) local NassetsTot=#self.assets local NassetsInS=self:CountAssetsInStock() - local NassetsQP, NassetsP, NassetsQ=self.airwing and self.airwing:CountAssetsOnMission(nil, self) or 0,0,0 - + 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) @@ -613,6 +616,8 @@ function SQUADRON:_CheckAssetStatus() if mission then local distance=asset.flightgroup and UTILS.MetersToNM(mission:GetTargetDistance(asset.flightgroup.group:GetCoordinate())) or 0 text=text..string.format(" Mission %s - %s: Status=%s, Dist=%.1f NM", mission.name, mission.type, mission.status, distance) + else + text=text.."Mission None" end -- Flight status. diff --git a/Moose Development/Moose/Utilities/Profiler.lua b/Moose Development/Moose/Utilities/Profiler.lua index 7dabbb068..a7803975b 100644 --- a/Moose Development/Moose/Utilities/Profiler.lua +++ b/Moose Development/Moose/Utilities/Profiler.lua @@ -74,16 +74,22 @@ PROFILER = { fTimeTotal = {}, eventHandler = {}, startTime = nil, - endTime = nil, runTime = nil, - sortBy = 1, logUnknown = false, lowCpsThres = 5, + fileName = "", } -PROFILER.sortBy=1 -- Sort reports by 0=Count, 1=Total time by function -PROFILER.logUnknown=true -- Log unknown functions -PROFILER.lowCpsThres=1 -- Skip results with less than X calls per second +--- Waypoint data. +-- @type PROFILER.Data +-- @field #string func The function name. +-- @field #string src The source file. +-- @field #number line The line number +-- @field #number count Number of function calls. +-- @field #number tm Total time in seconds. + +PROFILER.logUnknown=false -- Log unknown functions +PROFILER.lowCpsThres=5 -- Skip results with less than X calls per second PROFILER.fileName="_LuaProfiler.txt" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -99,9 +105,8 @@ function PROFILER.Start(Delay, Duration) BASE:ScheduleOnce(Delay, PROFILER.Start, 0, Duration) else - PROFILER.startTime=timer.getTime() - PROFILER.endTime=0 - PROFILER.runTime=0 + PROFILER.TstartGame=timer.getTime() + PROFILER.TstartOS=os.clock() -- Add event handler. world.addEventHandler(PROFILER.eventHandler) @@ -142,11 +147,10 @@ function PROFILER.Stop(Delay) -- Remove hook. debug.sethook() - -- Set end time. - PROFILER.endTime=timer.getTime() -- Run time. - PROFILER.runTime=PROFILER.endTime-PROFILER.startTime + PROFILER.runTimeGame=timer.getTime()-PROFILER.TstartGame + PROFILER.runTimeOS=os.clock()-PROFILER.TstartOS -- Show info. PROFILER.showInfo() @@ -208,13 +212,19 @@ end --- Get data. -- @param #function func Function. --- @param #boolean detailed Not used. -function PROFILER.getData(func, detailed) +-- @return #string Function name. +-- @return #string Source file name. +-- @return #string Line number. +-- @return #number Function time in seconds. +function PROFILER.getData(func) + local n=PROFILER.dInfo[func] + if n.what=="C" then - return n.name, "", "", PROFILER.fTimeTotal[func] + return n.name, "?", "?", PROFILER.fTimeTotal[func] end - return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func] + + return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func] end --- Write text to log file. @@ -225,24 +235,27 @@ function PROFILER._flog(f, txt) end --- Show table. --- @param #table t Data table. +-- @param #table data Data table. -- @param #function f The file. -- @param #boolean detailed Show detailed info. -function PROFILER.showTable(t, f, detailed) - for i=1, #t do +function PROFILER.showTable(data, f, detailed) + + -- Loop over data. + for i=1, #data do + local t=data[i] --#PROFILER.Data - local cps=t[i].count/PROFILER.runTime + -- Calls per second. + local cps=t.count/PROFILER.runTimeGame if (cps>=PROFILER.lowCpsThres) then - if (detailed==false) then - PROFILER._flog(f,"- Function: "..t[i].func..": "..tostring(t[i].count).." ("..string.format("%.01f",cps).."/sec) Time: "..string.format("%g",t[i].tm).." seconds") - else - PROFILER._flog(f,"- Function: "..t[i].func..": "..tostring(t[i].count).." ("..string.format("%.01f",cps).."/sec) "..tostring(t[i].src)..":"..tostring(t[i].line).." Time: "..string.format("%g",t[i].tm).." seconds") - end + -- Output + local text=string.format("%20s: %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)) + PROFILER._flog(f, text) end end + end --- Write info to output file. @@ -250,15 +263,15 @@ function PROFILER.showInfo() -- Output file. local file=lfs.writedir()..[[Logs\]]..PROFILER.fileName - local f=io.open(file, 'w') - - BASE:I(string.format("### Profiler: Writing result to file %s", file)) + local f=io.open(file, 'w') -- Gather data. + local Ttot=0 + local Calls=0 local t={} for func, count in pairs(PROFILER.Counters) do - local s,src,line,tm=PROFILER.getData(func, false) + local s,src,line,tm=PROFILER.getData(func) if PROFILER.logUnknown==true then if s==nil then s="" end @@ -272,39 +285,65 @@ function PROFILER.showInfo() count=count, tm=tm, } + Ttot=Ttot+tm + Calls=Calls+count end end - - -- Sort result. - if PROFILER.sortBy==0 then - table.sort(t, function(a,b) return a.count>b.count end ) - end - if (PROFILER.sortBy==1) then - table.sort(t, function(a,b) return a.tm>b.tm end ) - 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("* Total functions : %d", #t)) + env.info(string.format("* Total func calls : %d", Calls)) + env.info(string.format("* Writing to file : \"%s\"", file)) + env.info("**************************************************************************************************") + + -- Sort by total time. + table.sort(t, function(a,b) return a.tm>b.tm end) -- Write data. PROFILER._flog(f,"") - PROFILER._flog(f,"#### #### #### #### #### ##### #### #### #### #### ####") - PROFILER._flog(f,"#### #### #### ---- Profiler Report ---- #### #### ####") - PROFILER._flog(f,"#### Profiler Runtime: "..string.format("%.01f",PROFILER.runTime/60).." minutes") - PROFILER._flog(f,"#### #### #### #### #### ##### #### #### #### #### ####") - PROFILER._flog(f,"") - PROFILER.showTable(t, f, false) + PROFILER._flog(f,"************************************************************************************************************************") + PROFILER._flog(f,"************************************************************************************************************************") + PROFILER._flog(f,"************************************************************************************************************************") + PROFILER._flog(f,"") + PROFILER._flog(f,"-------------------------") + 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,"") + 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) + + -- Sort by number of calls. + table.sort(t, function(a,b) return a.count>b.count end) -- Detailed data. PROFILER._flog(f,"") - PROFILER._flog(f,"#### #### #### #### #### #### #### #### #### #### #### #### ####") - PROFILER._flog(f,"#### #### #### ---- Profiler Detailed Report ---- #### #### ####") - PROFILER._flog(f,"#### #### #### #### #### #### #### #### #### #### #### #### ####") + PROFILER._flog(f,"************************************************************************************************************************") + PROFILER._flog(f,"") + PROFILER._flog(f,"------------------------------") + PROFILER._flog(f,"---- Data Sorted by Calls ----") + PROFILER._flog(f,"------------------------------") PROFILER._flog(f,"") PROFILER.showTable(t, f, true) -- Closing. PROFILER._flog(f,"") - PROFILER._flog(f,"#### #### #### #### #### #### #### #### #### #### #### #### ####") - + PROFILER._flog(f,"************************************************************************************************************************") + PROFILER._flog(f,"************************************************************************************************************************") + PROFILER._flog(f,"************************************************************************************************************************") -- Close file. f:close() end