mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Merge branch 'develop' into FF/Ops
This commit is contained in:
commit
7d2746e7be
@ -902,7 +902,7 @@ end
|
||||
function DATABASE:_RegisterAirbases()
|
||||
|
||||
for DCSAirbaseId, DCSAirbase in pairs(world.getAirbases()) do
|
||||
|
||||
|
||||
-- Get the airbase name.
|
||||
local DCSAirbaseName = DCSAirbase:getName()
|
||||
|
||||
@ -914,7 +914,7 @@ function DATABASE:_RegisterAirbases()
|
||||
|
||||
-- Debug output.
|
||||
self:I(string.format("Register Airbase: %s, getID=%d, GetID=%d (unique=%d)", DCSAirbaseName, DCSAirbase:getID(), airbase:GetID(), airbase:GetID(true)))
|
||||
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **TAW CougarNL**, *funkyfranky*
|
||||
--
|
||||
--
|
||||
-- @module Utilities.PROFILER
|
||||
-- @image MOOSE.JPG
|
||||
-- @image Utils_Profiler.jpg
|
||||
|
||||
|
||||
--- PROFILER class.
|
||||
@ -26,67 +26,79 @@
|
||||
-- @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? *
|
||||
--- *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?*
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # The PROFILER Concept
|
||||
--
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
-- The modules **os** and **lfs** need to be desanizied.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- The modules **os**, **io** and **lfs** need to be desanizied. Comment out the lines
|
||||
--
|
||||
-- --sanitizeModule('os')
|
||||
-- --sanitizeModule('io')
|
||||
-- --sanitizeModule('lfs')
|
||||
--
|
||||
-- in your *"DCS World OpenBeta/Scripts/MissionScripting.lua"* file.
|
||||
--
|
||||
-- But be aware that these changes can make you system vulnerable to attacks.
|
||||
--
|
||||
-- # Disclaimer
|
||||
--
|
||||
-- **Profiling itself is CPU expensive!** Don't use this when you want to fly or host a mission.
|
||||
--
|
||||
--
|
||||
-- # Start
|
||||
--
|
||||
--
|
||||
-- The profiler can simply be started with the @{#PROFILER.Start}(*Delay, Duration*) function
|
||||
--
|
||||
--
|
||||
-- PROFILER.Start()
|
||||
--
|
||||
--
|
||||
-- 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 with the @{#PROFILER.Stop}(*Delay*) function
|
||||
--
|
||||
--
|
||||
-- PROFILER.Stop()
|
||||
--
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
--
|
||||
-- The profiler output is written to a file in your DCS home folder
|
||||
--
|
||||
--
|
||||
-- X:\User\<Your User Name>\Saved Games\DCS OpenBeta\Logs
|
||||
--
|
||||
--
|
||||
-- The default file name is "MooseProfiler.txt". If that file exists, the file name is "MooseProfiler-001.txt" etc.
|
||||
--
|
||||
--
|
||||
-- ## Data
|
||||
--
|
||||
--
|
||||
-- The data in the output file provides information on the functions that were called in the mission.
|
||||
--
|
||||
--
|
||||
-- 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.ThreshCPS=1.5
|
||||
--
|
||||
--
|
||||
-- With this setting, only functions which are called more than 1.5 times per second are displayed. The default setting is PROFILER.ThreshCPS=0.0 (no threshold).
|
||||
--
|
||||
--
|
||||
-- Furthermore, you can limit the output for functions that consumed a certain amount of CPU time in total by
|
||||
--
|
||||
--
|
||||
-- PROFILER.ThreshTtot=0.005
|
||||
--
|
||||
--
|
||||
-- With this setting, which is also the default, only functions which in total used more than 5 milliseconds CPU time.
|
||||
--
|
||||
--
|
||||
-- @field #PROFILER
|
||||
PROFILER = {
|
||||
ClassName = "PROFILER",
|
||||
@ -128,11 +140,11 @@ function PROFILER.Start(Delay, Duration)
|
||||
if not io then
|
||||
env.error("ERROR: Profiler needs io to be desanitized!")
|
||||
go=false
|
||||
end
|
||||
end
|
||||
if not lfs then
|
||||
env.error("ERROR: Profiler needs lfs to be desanitized!")
|
||||
go=false
|
||||
end
|
||||
end
|
||||
if not go then
|
||||
return
|
||||
end
|
||||
@ -140,14 +152,14 @@ function PROFILER.Start(Delay, Duration)
|
||||
if Delay and Delay>0 then
|
||||
BASE:ScheduleOnce(Delay, PROFILER.Start, 0, Duration)
|
||||
else
|
||||
|
||||
|
||||
-- Set start time.
|
||||
PROFILER.TstartGame=timer.getTime()
|
||||
PROFILER.TstartOS=os.clock()
|
||||
|
||||
|
||||
-- Add event handler.
|
||||
world.addEventHandler(PROFILER.eventHandler)
|
||||
|
||||
|
||||
-- Info in log.
|
||||
env.info('############################ Profiler Started ############################')
|
||||
if Duration then
|
||||
@ -160,22 +172,22 @@ function PROFILER.Start(Delay, Duration)
|
||||
env.info(string.format("- Output file \"%s\" in your DCS log file folder", PROFILER.getfilename(PROFILER.fileNameSuffix)))
|
||||
env.info(string.format("- Output file \"%s\" in CSV format", PROFILER.getfilename("csv")))
|
||||
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
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Stop profiler.
|
||||
@ -183,24 +195,24 @@ end
|
||||
function PROFILER.Stop(Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
|
||||
|
||||
BASE:ScheduleOnce(Delay, PROFILER.Stop)
|
||||
|
||||
|
||||
else
|
||||
|
||||
-- Remove hook.
|
||||
debug.sethook()
|
||||
|
||||
|
||||
|
||||
|
||||
-- Run time game.
|
||||
local runTimeGame=timer.getTime()-PROFILER.TstartGame
|
||||
|
||||
|
||||
-- Run time real OS.
|
||||
local runTimeOS=os.clock()-PROFILER.TstartOS
|
||||
|
||||
|
||||
-- Show info.
|
||||
PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@ -221,35 +233,35 @@ end
|
||||
function PROFILER.hook(event)
|
||||
|
||||
local f=debug.getinfo(2, "f").func
|
||||
|
||||
|
||||
if event=='call' then
|
||||
|
||||
|
||||
if PROFILER.Counters[f]==nil then
|
||||
|
||||
|
||||
PROFILER.Counters[f]=1
|
||||
PROFILER.dInfo[f]=debug.getinfo(2,"Sn")
|
||||
|
||||
|
||||
if PROFILER.fTimeTotal[f]==nil then
|
||||
PROFILER.fTimeTotal[f]=0
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
PROFILER.Counters[f]=PROFILER.Counters[f]+1
|
||||
end
|
||||
|
||||
|
||||
if PROFILER.fTime[f]==nil then
|
||||
PROFILER.fTime[f]=os.clock()
|
||||
end
|
||||
|
||||
|
||||
elseif (event=='return') then
|
||||
|
||||
|
||||
if PROFILER.fTime[f]~=nil then
|
||||
PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f])
|
||||
PROFILER.fTime[f]=nil
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -265,12 +277,12 @@ end
|
||||
function PROFILER.getData(func)
|
||||
|
||||
local n=PROFILER.dInfo[func]
|
||||
|
||||
|
||||
if n.what=="C" then
|
||||
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.
|
||||
@ -287,24 +299,24 @@ end
|
||||
function PROFILER.showTable(data, f, runTimeGame)
|
||||
|
||||
-- Loop over data.
|
||||
for i=1, #data do
|
||||
for i=1, #data do
|
||||
local t=data[i] --#PROFILER.Data
|
||||
|
||||
|
||||
-- Calls per second.
|
||||
local cps=t.count/runTimeGame
|
||||
|
||||
|
||||
local threshCPS=cps>=PROFILER.ThreshCPS
|
||||
local threshTot=t.tm>=PROFILER.ThreshTtot
|
||||
|
||||
|
||||
if threshCPS and threshTot then
|
||||
|
||||
|
||||
-- Output
|
||||
local text=string.format("%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
|
||||
PROFILER._flog(f, text)
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Print csv file.
|
||||
@ -319,20 +331,20 @@ function PROFILER.printCSV(data, runTimeGame)
|
||||
-- Header.
|
||||
local text="Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number,"
|
||||
g:write(text.."\r\n")
|
||||
|
||||
|
||||
-- Loop over data.
|
||||
for i=1, #data do
|
||||
for i=1, #data do
|
||||
local t=data[i] --#PROFILER.Data
|
||||
|
||||
|
||||
-- Calls per second.
|
||||
local cps=t.count/runTimeGame
|
||||
|
||||
-- Output
|
||||
local txt=string.format("%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
|
||||
g:write(txt.."\r\n")
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- Close file.
|
||||
g:close()
|
||||
end
|
||||
@ -344,23 +356,23 @@ end
|
||||
function PROFILER.getfilename(ext)
|
||||
|
||||
local dir=lfs.writedir()..[[Logs\]]
|
||||
|
||||
|
||||
ext=ext or PROFILER.fileNameSuffix
|
||||
|
||||
|
||||
local file=dir..PROFILER.fileNamePrefix.."."..ext
|
||||
|
||||
|
||||
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, ext)
|
||||
|
||||
if not UTILS.FileExists(file) then
|
||||
return file
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@ -372,30 +384,30 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
|
||||
-- Output file.
|
||||
local file=PROFILER.getfilename(PROFILER.fileNameSuffix)
|
||||
local f=io.open(file, 'w')
|
||||
|
||||
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)
|
||||
|
||||
|
||||
if PROFILER.logUnknown==true then
|
||||
if s==nil then s="<Unknown>" end
|
||||
end
|
||||
|
||||
|
||||
if s~=nil then
|
||||
|
||||
|
||||
-- Profile data.
|
||||
local T=
|
||||
{ func=s,
|
||||
@ -404,7 +416,7 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
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
|
||||
@ -419,49 +431,49 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
else
|
||||
tserialize.count=tserialize.count+T.count
|
||||
tserialize.tm=tserialize.tm+T.tm
|
||||
end
|
||||
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
|
||||
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
|
||||
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.
|
||||
-- Add special cases.
|
||||
if tcopy then
|
||||
table.insert(t, tcopy)
|
||||
end
|
||||
if tserialize then
|
||||
table.insert(t, tserialize)
|
||||
table.insert(t, tserialize)
|
||||
end
|
||||
if tforgen then
|
||||
table.insert(t, tforgen)
|
||||
end
|
||||
if tpairs then
|
||||
table.insert(t, tpairs)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
env.info('############################ Profiler Stopped ############################')
|
||||
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))
|
||||
@ -470,11 +482,11 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
env.info(string.format("* Total func calls : %d", Calls))
|
||||
env.info(string.format("* Writing to file : \"%s\"", file))
|
||||
env.info(string.format("* Writing to file : \"%s\"", PROFILER.getfilename("csv")))
|
||||
env.info("##############################################################################")
|
||||
|
||||
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,"************************************************************************************************************************")
|
||||
@ -493,7 +505,7 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
PROFILER._flog(f,string.format("* Total func calls = %d", Calls))
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,string.format("* Calls per second threshold = %.3f/sec", PROFILER.ThreshCPS))
|
||||
PROFILER._flog(f,string.format("* Total func time threshold = %.3f sec", PROFILER.ThreshTtot))
|
||||
PROFILER._flog(f,string.format("* Total func time threshold = %.3f sec", PROFILER.ThreshTtot))
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"")
|
||||
@ -501,7 +513,7 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
|
||||
-- Sort by number of calls.
|
||||
table.sort(t, function(a,b) return a.tm/a.count>b.tm/b.count end)
|
||||
|
||||
|
||||
-- Detailed data.
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
@ -511,10 +523,10 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
PROFILER._flog(f,"--------------------------------------")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER.showTable(t, f, runTimeGame)
|
||||
|
||||
|
||||
-- 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,"************************************************************************************************************************")
|
||||
@ -524,7 +536,7 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
PROFILER._flog(f,"------------------------------------")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER.showTable(t, f, runTimeGame)
|
||||
|
||||
|
||||
-- Closing.
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
@ -532,8 +544,7 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
-- Close file.
|
||||
f:close()
|
||||
|
||||
|
||||
-- Print csv file.
|
||||
PROFILER.printCSV(t, runTimeGame)
|
||||
end
|
||||
|
||||
|
||||
@ -461,19 +461,19 @@ function AIRBASE:Register(AirbaseName)
|
||||
|
||||
-- Inherit everything from positionable.
|
||||
local self=BASE:Inherit(self, POSITIONABLE:New(AirbaseName)) --#AIRBASE
|
||||
|
||||
|
||||
-- Set airbase name.
|
||||
self.AirbaseName=AirbaseName
|
||||
|
||||
|
||||
-- Set airbase ID.
|
||||
self.AirbaseID=self:GetID(true)
|
||||
|
||||
|
||||
-- Get descriptors.
|
||||
self.descriptors=self:GetDesc()
|
||||
|
||||
|
||||
-- Category.
|
||||
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
|
||||
|
||||
|
||||
-- Set category.
|
||||
if self.category==Airbase.Category.AIRDROME then
|
||||
self.isAirdrome=true
|
||||
@ -484,14 +484,14 @@ function AIRBASE:Register(AirbaseName)
|
||||
else
|
||||
self:E("ERROR: Unknown airbase category!")
|
||||
end
|
||||
|
||||
|
||||
self:_InitParkingSpots()
|
||||
|
||||
|
||||
local vec2=self:GetVec2()
|
||||
|
||||
|
||||
-- Init coordinate.
|
||||
self:GetCoordinate()
|
||||
|
||||
|
||||
if vec2 then
|
||||
-- TODO: For ships we need a moving zone.
|
||||
self.AirbaseZone=ZONE_RADIUS:New( AirbaseName, vec2, 2500 )
|
||||
@ -628,7 +628,7 @@ function AIRBASE:GetID(unique)
|
||||
local airbaseID=tonumber(DCSAirbase:getID())
|
||||
|
||||
local airbaseCategory=self:GetAirbaseCategory()
|
||||
|
||||
|
||||
if AirbaseName==self.AirbaseName then
|
||||
if airbaseCategory==Airbase.Category.SHIP or airbaseCategory==Airbase.Category.HELIPAD then
|
||||
-- Ships get a negative sign as their unit number might be the same as the ID of another airbase.
|
||||
@ -819,16 +819,16 @@ function AIRBASE:_InitParkingSpots()
|
||||
-- Init table.
|
||||
self.parking={}
|
||||
self.parkingByID={}
|
||||
|
||||
|
||||
self.NparkingTotal=0
|
||||
self.NparkingTerminal={}
|
||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||
self.NparkingTerminal[terminalType]=0
|
||||
end
|
||||
end
|
||||
|
||||
-- Put coordinates of parking spots into table.
|
||||
for _,spot in pairs(parkingdata) do
|
||||
|
||||
|
||||
-- New parking spot.
|
||||
local park={} --#AIRBASE.ParkingSpot
|
||||
park.Vec3=spot.vTerminalPos
|
||||
@ -839,13 +839,13 @@ function AIRBASE:_InitParkingSpots()
|
||||
park.TerminalID0=spot.Term_Index_0
|
||||
park.TerminalType=spot.Term_Type
|
||||
park.TOAC=spot.TO_AC
|
||||
|
||||
|
||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||
if self._CheckTerminalType(terminalType, park.TerminalType) then
|
||||
self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self.parkingByID[park.TerminalID]=park
|
||||
table.insert(self.parking, park)
|
||||
end
|
||||
@ -869,7 +869,7 @@ function AIRBASE:GetParkingSpotsTable(termtype)
|
||||
|
||||
-- Get parking data of all spots (free or occupied)
|
||||
local parkingdata=self:GetParkingData(false)
|
||||
|
||||
|
||||
-- Get parking data of all free spots.
|
||||
local parkingfree=self:GetParkingData(true)
|
||||
|
||||
@ -886,17 +886,17 @@ function AIRBASE:GetParkingSpotsTable(termtype)
|
||||
-- Put coordinates of parking spots into table.
|
||||
local spots={}
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
|
||||
|
||||
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then
|
||||
|
||||
|
||||
local spot=self:_GetParkingSpotByID(_spot.Term_Index)
|
||||
|
||||
|
||||
spot.Free=_isfree(_spot) -- updated
|
||||
spot.TOAC=_spot.TO_AC -- updated
|
||||
|
||||
|
||||
table.insert(spots, spot)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
return spots
|
||||
@ -917,14 +917,14 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
|
||||
for _,_spot in pairs(parkingfree) do
|
||||
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) and _spot.Term_Index>0 then
|
||||
if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then
|
||||
|
||||
|
||||
local spot=self:_GetParkingSpotByID(_spot.Term_Index)
|
||||
|
||||
spot.Free=true -- updated
|
||||
spot.TOAC=_spot.TO_AC -- updated
|
||||
|
||||
|
||||
table.insert(freespots, spot)
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1245,7 +1245,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
|
||||
-- Get spawn points on runway. These can be used to determine the runway heading.
|
||||
local runwaycoords=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway)
|
||||
|
||||
|
||||
-- Debug: For finding the numbers of the spawn points belonging to each runway.
|
||||
if false then
|
||||
for i,_coord in pairs(runwaycoords) do
|
||||
@ -1264,7 +1264,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
|
||||
-- Airbase name.
|
||||
local name=self:GetName()
|
||||
|
||||
|
||||
|
||||
-- Exceptions
|
||||
if name==AIRBASE.Nevada.Jean_Airport or
|
||||
@ -1277,35 +1277,35 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
|
||||
-- 1-->4, 2-->3, 3-->2, 4-->1
|
||||
exception=1
|
||||
|
||||
elseif UTILS.GetDCSMap()==DCSMAP.Syria and N>=2 and
|
||||
|
||||
elseif UTILS.GetDCSMap()==DCSMAP.Syria and N>=2 and
|
||||
name~=AIRBASE.Syria.Minakh and
|
||||
name~=AIRBASE.Syria.Damascus and
|
||||
name~=AIRBASE.Syria.Khalkhalah and
|
||||
name~=AIRBASE.Syria.Marj_Ruhayyil and
|
||||
name~=AIRBASE.Syria.Beirut_Rafic_Hariri then
|
||||
|
||||
|
||||
-- 1-->3, 2-->4, 3-->1, 4-->2
|
||||
exception=2
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
local function f(i)
|
||||
|
||||
local j
|
||||
|
||||
|
||||
if exception==1 then
|
||||
|
||||
|
||||
j=N-(i+1) -- 1-->4, 2-->3
|
||||
|
||||
|
||||
elseif exception==2 then
|
||||
|
||||
|
||||
if i<=N2 then
|
||||
j=i+N2 -- 1-->3, 2-->4
|
||||
else
|
||||
j=i-N2 -- 3-->1, 4-->3
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
|
||||
if i%2==0 then
|
||||
@ -1313,9 +1313,9 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
else
|
||||
j=i+1 -- odd 1-->2, 3-->4
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- Special case where there is no obvious order.
|
||||
if name==AIRBASE.Syria.Beirut_Rafic_Hariri then
|
||||
if i==1 then
|
||||
@ -1348,7 +1348,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
j=2
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return j
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
--- **Wrapper** - Markers On the F10 map.
|
||||
--
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Convenient handling of markers via multiple user API functions.
|
||||
@ -8,7 +8,7 @@
|
||||
-- * Retrieve data such as text and coordinate.
|
||||
-- * Marker specific FSM events when a marker is added, removed or changed.
|
||||
-- * Additional FSM events when marker text or position is changed.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
@ -36,104 +36,104 @@
|
||||
-- 
|
||||
--
|
||||
-- # The MARKER Class Idea
|
||||
--
|
||||
--
|
||||
-- The MARKER class simplifies creating, updating and removing of markers on the F10 map.
|
||||
--
|
||||
--
|
||||
-- # Create a Marker
|
||||
--
|
||||
--
|
||||
-- -- Create a MARKER object at Batumi with a trivial text.
|
||||
-- local Coordinate=AIRBASE:FindByName("Batumi"):GetCoordinate()
|
||||
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield")
|
||||
--
|
||||
-- Now this does **not** show the marker yet. We still need to specifiy to whom it is shown. There are several options, i.e.
|
||||
--
|
||||
-- Now this does **not** show the marker yet. We still need to specifiy to whom it is shown. There are several options, i.e.
|
||||
-- show the marker to everyone, to a speficic coaliton only, or only to a specific group.
|
||||
--
|
||||
--
|
||||
-- ## For Everyone
|
||||
--
|
||||
--
|
||||
-- If the marker should be visible to everyone, you can use the :ToAll() function.
|
||||
--
|
||||
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield"):ToAll()
|
||||
--
|
||||
--
|
||||
-- ## For a Coaliton
|
||||
--
|
||||
--
|
||||
-- If the maker should be visible to a specific coalition, you can use the :ToCoalition() function.
|
||||
--
|
||||
--
|
||||
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield"):ToCoaliton(coaliton.side.BLUE)
|
||||
--
|
||||
--
|
||||
-- ### To Blue Coaliton
|
||||
--
|
||||
--
|
||||
-- ### To Red Coalition
|
||||
--
|
||||
--
|
||||
-- This would show the marker only to the Blue coaliton.
|
||||
--
|
||||
--
|
||||
-- ## For a Group
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- # Removing a Marker
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- # Updating a Marker
|
||||
--
|
||||
--
|
||||
-- The marker text and coordinate can be updated easily as shown below.
|
||||
--
|
||||
--
|
||||
-- However, note that **updateing involves to remove and recreate the marker if either text or its coordinate is changed**.
|
||||
-- *This is a DCS scripting engine limitation.*
|
||||
--
|
||||
-- *This is a DCS scripting engine limitation.*
|
||||
--
|
||||
-- ## Update Text
|
||||
--
|
||||
--
|
||||
-- If you created a marker "mymarker" as shown above, you can update the dispayed test by
|
||||
--
|
||||
--
|
||||
-- mymarker:UpdateText("I am the new text at Batumi")
|
||||
--
|
||||
--
|
||||
-- The update can also be delayed by, e.g. 90 seconds, using
|
||||
--
|
||||
--
|
||||
-- mymarker:UpdateText("I am the new text at Batumi", 90)
|
||||
--
|
||||
--
|
||||
-- ## Update Coordinate
|
||||
--
|
||||
--
|
||||
-- If you created a marker "mymarker" as shown above, you can update its coordinate on the F10 map by
|
||||
--
|
||||
--
|
||||
-- mymarker:UpdateCoordinate(NewCoordinate)
|
||||
--
|
||||
--
|
||||
-- The update can also be delayed by, e.g. 60 seconds, using
|
||||
--
|
||||
--
|
||||
-- mymarker:UpdateCoordinate(NewCoordinate, 60)
|
||||
--
|
||||
--
|
||||
-- # Retrieve Data
|
||||
--
|
||||
--
|
||||
-- The important data as the displayed text and the coordinate of the marker can be retrieved easily.
|
||||
--
|
||||
--
|
||||
-- ## Text
|
||||
--
|
||||
--
|
||||
-- local text=mymarker:GetText()
|
||||
-- env.info("Marker Text = " .. text)
|
||||
--
|
||||
--
|
||||
-- ## Coordinate
|
||||
--
|
||||
--
|
||||
-- local Coordinate=mymarker:GetCoordinate()
|
||||
-- env.info("Marker Coordinate LL DSM = " .. Coordinate:ToStringLLDMS())
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- # FSM Events
|
||||
--
|
||||
--
|
||||
-- Moose creates addditonal events, so called FSM event, when markers are added, changed, removed, and text or the coordianteis updated.
|
||||
--
|
||||
--
|
||||
-- These events can be captured and used for processing via OnAfter functions as shown below.
|
||||
--
|
||||
--
|
||||
-- ## Added
|
||||
--
|
||||
--
|
||||
-- ## Changed
|
||||
--
|
||||
--
|
||||
-- ## Removed
|
||||
--
|
||||
--
|
||||
-- ## TextUpdate
|
||||
--
|
||||
--
|
||||
-- ## CoordUpdate
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- # Examples
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @field #MARKER
|
||||
MARKER = {
|
||||
ClassName = "MARKER",
|
||||
@ -170,29 +170,29 @@ MARKER.version="0.1.0"
|
||||
--- Create a new MARKER class object.
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to place the marker.
|
||||
-- @param #string Text Text displayed on the mark panel.
|
||||
-- @param #string Text Text displayed on the mark panel.
|
||||
-- @return #MARKER self
|
||||
function MARKER:New(Coordinate, Text)
|
||||
|
||||
-- Inherit everything from FSM class.
|
||||
local self=BASE:Inherit(self, FSM:New()) -- #MARKER
|
||||
|
||||
|
||||
self.coordinate=Coordinate
|
||||
|
||||
|
||||
self.text=Text
|
||||
|
||||
|
||||
-- Defaults
|
||||
self.readonly=false
|
||||
self.message=""
|
||||
|
||||
-- New marker ID. This is not the one of the actual marker.
|
||||
|
||||
-- New marker ID. This is not the one of the actual marker.
|
||||
_MARKERID=_MARKERID+1
|
||||
|
||||
|
||||
self.myid=_MARKERID
|
||||
|
||||
|
||||
-- Log ID.
|
||||
self.lid=string.format("Marker #%d | ", self.myid)
|
||||
|
||||
|
||||
-- Start State.
|
||||
self:SetStartState("Invisible")
|
||||
|
||||
@ -201,7 +201,7 @@ function MARKER:New(Coordinate, Text)
|
||||
self:AddTransition("Invisible", "Added", "Visible") -- Marker was added.
|
||||
self:AddTransition("Visible", "Removed", "Invisible") -- Marker was removed.
|
||||
self:AddTransition("*", "Changed", "*") -- Marker was changed.
|
||||
|
||||
|
||||
self:AddTransition("*", "TextUpdate", "*") -- Text updated.
|
||||
self:AddTransition("*", "CoordUpdate", "*") -- Coordinates updated.
|
||||
|
||||
@ -304,8 +304,8 @@ function MARKER:New(Coordinate, Text)
|
||||
self:HandleEvent(EVENTS.MarkAdded)
|
||||
self:HandleEvent(EVENTS.MarkRemoved)
|
||||
self:HandleEvent(EVENTS.MarkChange)
|
||||
|
||||
return self
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -318,7 +318,7 @@ end
|
||||
function MARKER:ReadOnly()
|
||||
|
||||
self.readonly=true
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -329,7 +329,7 @@ end
|
||||
function MARKER:Message(Text)
|
||||
|
||||
self.message=Text or ""
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -349,14 +349,14 @@ function MARKER:ToAll(Delay)
|
||||
self.togroup=nil
|
||||
self.groupname=nil
|
||||
self.groupid=nil
|
||||
|
||||
|
||||
-- First remove an existing mark.
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
|
||||
self.mid=UTILS.GetMarkID()
|
||||
|
||||
|
||||
-- Call DCS function.
|
||||
trigger.action.markToAll(self.mid, self.text, self.coordinate:GetVec3(), self.readonly, self.message)
|
||||
|
||||
@ -377,25 +377,25 @@ function MARKER:ToCoalition(Coalition, Delay)
|
||||
else
|
||||
|
||||
self.coalition=Coalition
|
||||
|
||||
|
||||
self.tocoaliton=true
|
||||
self.toall=false
|
||||
self.togroup=false
|
||||
self.groupname=nil
|
||||
self.groupid=nil
|
||||
|
||||
|
||||
-- First remove an existing mark.
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
|
||||
self.mid=UTILS.GetMarkID()
|
||||
|
||||
|
||||
-- Call DCS function.
|
||||
trigger.action.markToCoalition(self.mid, self.text, self.coordinate:GetVec3(), self.coalition, self.readonly, self.message)
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -440,36 +440,36 @@ function MARKER:ToGroup(Group, Delay)
|
||||
|
||||
-- Check if group exists.
|
||||
if Group and Group:IsAlive()~=nil then
|
||||
|
||||
|
||||
self.groupid=Group:GetID()
|
||||
|
||||
|
||||
if self.groupid then
|
||||
|
||||
|
||||
self.groupname=Group:GetName()
|
||||
|
||||
|
||||
self.togroup=true
|
||||
self.tocoaliton=nil
|
||||
self.coalition=nil
|
||||
self.toall=nil
|
||||
|
||||
|
||||
-- First remove an existing mark.
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
|
||||
self.mid=UTILS.GetMarkID()
|
||||
|
||||
|
||||
-- Call DCS function.
|
||||
trigger.action.markToGroup(self.mid, self.text, self.coordinate:GetVec3(), self.groupid, self.readonly, self.message)
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
--TODO: Warning!
|
||||
--TODO: Warning!
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -482,14 +482,14 @@ function MARKER:UpdateText(Text, Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.UpdateText, self, Text)
|
||||
else
|
||||
else
|
||||
|
||||
self.text=tostring(Text)
|
||||
|
||||
|
||||
self:Refresh()
|
||||
|
||||
|
||||
self:TextUpdate(tostring(Text))
|
||||
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
@ -504,14 +504,14 @@ function MARKER:UpdateCoordinate(Coordinate, Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.UpdateCoordinate, self, Coordinate)
|
||||
else
|
||||
else
|
||||
|
||||
self.coordinate=Coordinate
|
||||
|
||||
|
||||
self:Refresh()
|
||||
|
||||
|
||||
self:CoordUpdate(Coordinate)
|
||||
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
@ -525,26 +525,26 @@ function MARKER:Refresh(Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.Refresh, self)
|
||||
else
|
||||
else
|
||||
|
||||
if self.toall then
|
||||
|
||||
|
||||
self:ToAll()
|
||||
|
||||
|
||||
elseif self.tocoaliton then
|
||||
|
||||
|
||||
self:ToCoalition(self.coalition)
|
||||
|
||||
|
||||
elseif self.togroup then
|
||||
|
||||
|
||||
local group=GROUP:FindByName(self.groupname)
|
||||
|
||||
|
||||
self:ToGroup(group)
|
||||
|
||||
|
||||
else
|
||||
self:E(self.lid.."ERROR: unknown To in :Refresh()!")
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
@ -564,9 +564,9 @@ function MARKER:Remove(Delay)
|
||||
|
||||
-- Call DCS function.
|
||||
trigger.action.removeMark(self.mid)
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
@ -605,7 +605,7 @@ end
|
||||
|
||||
--- Check if marker is currently invisible on the F10 map.
|
||||
-- @param #MARKER self
|
||||
-- @return
|
||||
-- @return
|
||||
function MARKER:IsInvisible()
|
||||
return self:Is("Invisible")
|
||||
end
|
||||
@ -620,17 +620,17 @@ end
|
||||
function MARKER:OnEventMarkAdded(EventData)
|
||||
|
||||
if EventData and EventData.MarkID then
|
||||
|
||||
|
||||
local MarkID=EventData.MarkID
|
||||
|
||||
|
||||
self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s", tostring(MarkID)))
|
||||
|
||||
|
||||
if MarkID==self.mid then
|
||||
|
||||
|
||||
self.shown=true
|
||||
|
||||
|
||||
self:Added(EventData)
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@ -643,21 +643,21 @@ end
|
||||
function MARKER:OnEventMarkRemoved(EventData)
|
||||
|
||||
if EventData and EventData.MarkID then
|
||||
|
||||
|
||||
local MarkID=EventData.MarkID
|
||||
|
||||
|
||||
self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s", tostring(MarkID)))
|
||||
|
||||
|
||||
if MarkID==self.mid then
|
||||
|
||||
|
||||
self.shown=false
|
||||
|
||||
|
||||
self:Removed(EventData)
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Event function when a MARKER changed.
|
||||
@ -666,17 +666,17 @@ end
|
||||
function MARKER:OnEventMarkChange(EventData)
|
||||
|
||||
if EventData and EventData.MarkID then
|
||||
|
||||
|
||||
local MarkID=EventData.MarkID
|
||||
|
||||
|
||||
self:T3(self.lid..string.format("Captured event MarkChange for Mark ID=%s", tostring(MarkID)))
|
||||
|
||||
|
||||
if MarkID==self.mid then
|
||||
|
||||
|
||||
self:Changed(EventData)
|
||||
|
||||
|
||||
self:TextChanged(tostring(EventData.MarkText))
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@ -696,7 +696,7 @@ end
|
||||
function MARKER:onafterAdded(From, Event, To, EventData)
|
||||
|
||||
-- Debug info.
|
||||
local text=string.format("Captured event MarkAdded for myself:\n")
|
||||
local text=string.format("Captured event MarkAdded for myself:\n")
|
||||
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
|
||||
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
||||
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
|
||||
@ -716,7 +716,7 @@ end
|
||||
function MARKER:onafterRemoved(From, Event, To, EventData)
|
||||
|
||||
-- Debug info.
|
||||
local text=string.format("Captured event MarkRemoved for myself:\n")
|
||||
local text=string.format("Captured event MarkRemoved for myself:\n")
|
||||
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
|
||||
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
||||
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
|
||||
@ -736,7 +736,7 @@ end
|
||||
function MARKER:onafterChanged(From, Event, To, EventData)
|
||||
|
||||
-- Debug info.
|
||||
local text=string.format("Captured event MarkChange for myself:\n")
|
||||
local text=string.format("Captured event MarkChange for myself:\n")
|
||||
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
|
||||
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
||||
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
|
||||
@ -768,7 +768,7 @@ end
|
||||
function MARKER:onafterCoordUpdate(From, Event, To, Coordinate)
|
||||
|
||||
self:T(self.lid..string.format("New Marker Coordinate in LL DMS: %s", Coordinate:ToStringLLDMS()))
|
||||
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user