mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-07-23 08:29:30 +00:00
688 lines
21 KiB
Lua
688 lines
21 KiB
Lua
--- **Functional** - (R2.5) - Monitor and export flight model data.
|
|
--
|
|
-- Record flight data and export it to a csv file.
|
|
--
|
|
-- ## Recorded Data:
|
|
--
|
|
-- * Time,
|
|
-- * Altitude,
|
|
-- * Temperature,
|
|
-- * Pressure,
|
|
-- * Velocity (total and x,y,z components),
|
|
-- * Acceleration (total and x,y,z components),
|
|
-- * AoA,
|
|
-- * Pitch and pitch rate,
|
|
-- * Roll and roll rate,
|
|
-- * Yaw and yaw rate,
|
|
-- * Turn rate.
|
|
--
|
|
-- ===
|
|
--
|
|
-- ### Author: **funkyfranky**
|
|
-- @module Functional.FMData
|
|
-- @image Functional_Rat2.png
|
|
|
|
|
|
--- FMD class.
|
|
-- @type FMD
|
|
-- @field #string ClassName Name of the class.
|
|
-- @field #boolean Debug Debug mode. Messages to all about status.
|
|
-- @field #string lid Class id string for output to DCS log file.
|
|
-- @field #table players Player table.
|
|
-- @field #table menuadded Table of units where the F10 radio menu was added.
|
|
-- @field #string savepath Path to save data files.
|
|
-- @extends Core.Fsm#FSM
|
|
|
|
--- Be sure!
|
|
--
|
|
-- ===
|
|
--
|
|
-- 
|
|
--
|
|
-- # The FMD Concept
|
|
--
|
|
-- This class can be used to record flight data such as velocity, acceleration, pitch, roll, yaw and turn rates. The output is written to a csv file for later analysis.
|
|
--
|
|
-- # Usage
|
|
--
|
|
-- The script is very easy to use. It only requires the line
|
|
--
|
|
-- fmd=FMD:New()
|
|
--
|
|
-- This will automatically start the FMD script.
|
|
--
|
|
-- Each player will get an entry "FMD" in the F-10 radio menu. There he can start or stop the data recording.
|
|
--
|
|
-- **IMPORTANT**
|
|
-- Due to a DCS bug, it is necessary that (in single player mode), player/clients have to **hit ESC twice** before entering an aircraft client slot.
|
|
-- Otherwise, the script will not load and now menus will be created for the player.
|
|
--
|
|
-- # Output
|
|
--
|
|
-- After the data recording is completed, the data is written to a csv file. The file name starts with **FMD** and contains the employed airframe plus a running number.
|
|
--
|
|
-- However, one must desanitize the **io** and **lfs** lines the DCS root directory. Otherwise, DCS will not allow data to be written to file.
|
|
--
|
|
-- @field #FMD
|
|
FMD = {
|
|
ClassName = "FMD",
|
|
Debug = false,
|
|
lid = nil,
|
|
players = {},
|
|
menuadded = {},
|
|
savepath = nil,
|
|
}
|
|
|
|
--- Player data table.
|
|
-- @type FMD.PlayerData
|
|
-- @field Core.Scheduler#SCHEDULER scheduler Scheduler
|
|
-- @field Wrapper.Unit#UNIT unit Player unit.
|
|
-- @field #string unitname Name of the unit.
|
|
-- @field Wrapper.Client#CLIENT client Player client.
|
|
-- @field #string actype Aircraft type.
|
|
-- @field #string name Player name.
|
|
-- @field #number dt Time step for data recording in seconds. Default 0.01 sec ==> 100 data points per second!
|
|
-- @field #number rd Recording duration in seconds.
|
|
-- @field #boolean recording If true, recording started.
|
|
-- @field #table data Data table.
|
|
-- @field #number SID Scheduler ID.
|
|
|
|
--- Data point table.
|
|
-- @type FMD.DataPoint
|
|
-- @field #number time Abs mission time.
|
|
-- @field #number T Temperature in degrees Celsius.
|
|
-- @field #number P Pressure.
|
|
-- @field #number Alt Altitude ASL in meters.
|
|
-- @field #number AoA Angle of Attack in degrees.
|
|
-- @field #number Pitch Pitch angle in degrees.
|
|
-- @field #number Roll Roll angle in degrees.
|
|
-- @field #number Yaw Yaw angle in degrees.
|
|
-- @field #number Climbrate Climb rate in m/s.
|
|
-- @field #number Vtot Total velocity in m/s.
|
|
-- @field DCS#Vec3 v Velocity vector. Components x,y,z in m/s.
|
|
-- @field DCS#Vec3 o Orientation vector. Components x,y,z in meters.
|
|
-- @field #number omega Angle velocity in m/s.
|
|
-- @field #number Hdg Heading in degrees.
|
|
-- @field #number Atot Total acceleration m/s^2.
|
|
-- @field DCS#Vec3 a Acceleration vector.
|
|
-- @field #number DRoll Roll speed in degrees/second.
|
|
-- @field #number DPitch Pitch speed in degrees/second.
|
|
-- @field #number DYaw Yaw speed in degrees/second.
|
|
|
|
--- Main group level radio menu: F10 Other/FMD.
|
|
-- @field #table MenuF10
|
|
FMD.MenuF10={}
|
|
|
|
--- FMD mission level F10 root menu.
|
|
-- @field #table MenuF10Root
|
|
FMD.MenuF10Root=nil
|
|
|
|
--- FMD script version.
|
|
-- @field #string version
|
|
FMD.version="0.1.0"
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Constructor
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- Create a new FMD class object.
|
|
-- @param #FMD self
|
|
-- @return #FMD self.
|
|
function FMD:New()
|
|
|
|
-- Inherit everthing from FSM class.
|
|
local self=BASE:Inherit(self, FSM:New()) -- #FMD
|
|
|
|
-- Start State.
|
|
self:SetStartState("Stopped")
|
|
|
|
-- Log string.
|
|
self.lid="FMD | "
|
|
|
|
-- Add FSM transitions.
|
|
-- From State --> Event --> To State
|
|
self:AddTransition("Stopped", "Start", "Running") -- Start FMD script.
|
|
self:AddTransition("*", "Status", "*") -- Start FMD script.
|
|
|
|
-- Start FMD.
|
|
FMD:Start()
|
|
|
|
return self
|
|
end
|
|
|
|
--- On after Start event.
|
|
-- @param #FMD self
|
|
-- @param #string From From state.
|
|
-- @param #string Event Event.
|
|
-- @param #string To To state.
|
|
function FMD:onafterStart(From, Event, To)
|
|
|
|
-- Short info.
|
|
local text=string.format("Starting Flight Model Data script version %s", FMD.version)
|
|
self:I(self.lid..text)
|
|
|
|
-- Handle events.
|
|
self:HandleEvent(EVENTS.Birth)
|
|
end
|
|
|
|
--- On after Stop event.
|
|
-- @param #FMD self
|
|
-- @param #string From From state.
|
|
-- @param #string Event Event.
|
|
-- @param #string To To state.
|
|
function FMD:onafterStop(From, Event, To)
|
|
|
|
-- Short info.
|
|
local text=string.format("Stopping Flight Model Data script version %s", FMD.version)
|
|
self:I(self.lid..text)
|
|
|
|
-- Handle events.
|
|
self:UnHandleEvent(EVENTS.Birth)
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Event Functions
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- FMD event handler for event birth.
|
|
-- @param #FMD self
|
|
-- @param Core.Event#EVENTDATA EventData
|
|
function FMD:OnEventBirth(EventData)
|
|
self:F3({eventbirth = EventData})
|
|
|
|
local _unitName=EventData.IniUnitName
|
|
local _unit, _playername=self:_GetPlayerUnitAndName(_unitName)
|
|
|
|
self:T2(self.lid.."BIRTH: unit = "..tostring(EventData.IniUnitName))
|
|
self:T2(self.lid.."BIRTH: group = "..tostring(EventData.IniGroupName))
|
|
self:T2(self.lid.."BIRTH: player = "..tostring(_playername))
|
|
|
|
if _unit and _playername then
|
|
|
|
local _uid=_unit:GetID()
|
|
local _group=_unit:GetGroup()
|
|
local _callsign=_unit:GetCallsign()
|
|
|
|
-- Debug output.
|
|
local text=string.format("Pilot %s, callsign %s entered unit %s of group %s.", _playername, _callsign, _unitName, _group:GetName())
|
|
self:T(self.lid..text)
|
|
MESSAGE:New(text, 5):ToAllIf(self.Debug)
|
|
|
|
-- Add Menu commands.
|
|
self:_AddF10Commands(_unitName)
|
|
|
|
-- Player data.
|
|
self.players[_playername]={}
|
|
|
|
local player=self.players[_playername] --#FMD.PlayerData
|
|
|
|
player.scheduler=SCHEDULER:New()
|
|
player.unit=_unit
|
|
player.unitname=_unitName
|
|
player.name=_playername
|
|
player.client=CLIENT:FindByName(_unitName, nil, true)
|
|
player.actype=_unit:GetTypeName()
|
|
player.dt=0.01
|
|
player.rd=30
|
|
player.recording=false
|
|
player.data={}
|
|
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Data Functions
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- Get data of unit.
|
|
-- @param #FMD self
|
|
-- @param Wrapper.Unit#UNIT unit
|
|
-- @return #FMD.DataPoint Datapoint.
|
|
function FMD:_GetDataPoint(unit)
|
|
|
|
-- Current coordinate.
|
|
local coord=unit:GetCoordinate()
|
|
|
|
local dp={} --#FMD.DataPoint
|
|
|
|
dp.a={}
|
|
dp.Alt=coord.y
|
|
dp.AoA=unit:GetAoA()
|
|
dp.Atot=nil
|
|
dp.P=coord:GetPressure()
|
|
dp.Pitch=unit:GetPitch()
|
|
dp.Roll=unit:GetRoll()
|
|
dp.time=timer.getAbsTime()
|
|
dp.T=coord:GetTemperature()
|
|
dp.v=unit:GetVelocityVec3()
|
|
dp.Vtot=UTILS.VecNorm(dp.v)
|
|
dp.Yaw=unit:GetYaw()
|
|
dp.o=unit:GetOrientationX()
|
|
dp.Hdg=unit:GetHeading()
|
|
|
|
return dp
|
|
end
|
|
|
|
--- Get data of unit.
|
|
-- @param #FMD self
|
|
-- @param #FMD.PlayerData playerData Player data.
|
|
-- @return #FMD.DataPoint Datapoint.
|
|
function FMD:_Derivative(playerData)
|
|
|
|
local function numderiv(fpm,fpp,h)
|
|
return 0.5*(fpp-fpm)/h
|
|
end
|
|
|
|
local datapoints=playerData.data
|
|
--local dt=playerData.dt
|
|
|
|
for i=2,#datapoints-1 do
|
|
|
|
local dpm=datapoints[i-1] --#FMD.DataPoint
|
|
local dpp=datapoints[i+1] --#FMD.DataPoint
|
|
local dpi=datapoints[i] --#FMD.DataPoint
|
|
|
|
-- Time step.
|
|
local dt=0.5*(dpp.time-dpm.time)
|
|
|
|
dpi.Atot=numderiv(dpm.Vtot, dpp.Vtot, dt)
|
|
|
|
dpi.a.x=numderiv(dpm.v.x, dpp.v.x, dt)
|
|
dpi.a.y=numderiv(dpm.v.y, dpp.v.y, dt)
|
|
dpi.a.z=numderiv(dpm.v.z, dpp.v.z, dt)
|
|
|
|
dpi.DPitch=numderiv(dpm.Pitch, dpp.Pitch, dt)
|
|
dpi.DRoll=numderiv(dpm.Roll, dpp.Roll, dt)
|
|
dpi.DYaw=numderiv(dpm.Yaw, dpp.Yaw, dt)
|
|
|
|
local ang=UTILS.VecAngle(dpm.o, dpp.o)
|
|
dpi.omega=numderiv(0, ang, dt)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
--- Record data
|
|
-- @param #FMD self
|
|
-- @param #FMD.PlayerData playerData Player data table.
|
|
function FMD:_RecordData(playerData)
|
|
|
|
-- Check if we are recording already.
|
|
if not playerData.recording then
|
|
|
|
-- Inform player.
|
|
local text=string.format("Data recording starts for %.1f sec.", playerData.rd)
|
|
MESSAGE:New(text, 3, "FMD"):ToClient(playerData.client)
|
|
|
|
-- Activate recording switch.
|
|
playerData.recording=true
|
|
end
|
|
|
|
-- Get data point.
|
|
local dp=self:_GetDataPoint(playerData.unit)
|
|
|
|
-- Add data point to player table.
|
|
table.insert(playerData.data, dp)
|
|
|
|
end
|
|
|
|
--- Save data.
|
|
-- @param #FMD self
|
|
-- @param #FMD.PlayerData playerData Player data table.
|
|
function FMD:_SaveData(playerData)
|
|
|
|
-- Nothing to save.
|
|
if playerData==nil or #playerData.data==0 or not io then
|
|
return
|
|
end
|
|
|
|
--- Function that saves data to file
|
|
local function _savefile(filename, data)
|
|
local f = assert(io.open(filename, "wb"))
|
|
f:write(data)
|
|
f:close()
|
|
end
|
|
|
|
-- Set path or default.
|
|
local path=self.savepath
|
|
if lfs then
|
|
path=path or lfs.writedir()
|
|
end
|
|
|
|
-- Create unused file name.
|
|
local filename=nil
|
|
for i=1,9999 do
|
|
|
|
-- Create file name.
|
|
filename=string.format("FMD-%s_%s-%04d.csv", playerData.name, playerData.actype, i)
|
|
|
|
-- Set path.
|
|
if path~=nil then
|
|
filename=path.."\\"..filename
|
|
end
|
|
|
|
-- Check if file exists.
|
|
local _exists=UTILS.FileExists(filename)
|
|
if not _exists then
|
|
break
|
|
end
|
|
end
|
|
|
|
|
|
-- Info
|
|
local text=string.format("Saving player %s flight data to file %s", playerData.name, filename)
|
|
self:I(self.lid..text)
|
|
|
|
-- Header line
|
|
local data="#Time,Altitude,Temperature,Pressure,Vtot,Vx,Vy,Vz,Atot,ax,ay,az,AoA,Pitch,dPitch/dt,Roll,dRoll/dt,Yaw,dYaw/dt\n"
|
|
|
|
local g0=playerData.data[1] --#FMD.DataPoint
|
|
local T0=g0.time
|
|
|
|
-- Calculate derivatives.
|
|
self:_Derivative(playerData)
|
|
|
|
for i=2,#playerData.data-1 do
|
|
|
|
local dp=playerData.data[i] --#FMD.DataPoint
|
|
|
|
-- Conversion m/s == Mach.
|
|
local ms2mach=0.00291545
|
|
|
|
local t=(dp.time-T0) or 0
|
|
local a=dp.Alt or 0
|
|
local b=dp.T or 0
|
|
local c=dp.P or 0
|
|
local d=dp.Vtot or 0
|
|
local e=dp.v.x or 0
|
|
local f=dp.v.y or 0
|
|
local g=dp.v.z or 0
|
|
local h=dp.Atot or 0
|
|
local i=dp.a.x or 0
|
|
local j=dp.a.y or 0
|
|
local k=dp.a.z or 0
|
|
local l=dp.AoA or 0
|
|
local m=dp.Pitch or 0
|
|
local n=dp.DPitch or 0
|
|
local o=dp.Roll or 0
|
|
local p=dp.DRoll or 0
|
|
local q=dp.Yaw or 0
|
|
local r=dp.DYaw or 0
|
|
local s=dp.omega or 0
|
|
|
|
-- Debug output.
|
|
self:T3(t)
|
|
self:T3(a)
|
|
self:T3(b)
|
|
self:T3(c)
|
|
self:T3(d)
|
|
self:T3(e)
|
|
self:T3(f)
|
|
self:T3(g)
|
|
self:T3(h)
|
|
self:T3(i)
|
|
self:T3(j)
|
|
self:T3(k)
|
|
self:T3(l)
|
|
self:T3(m)
|
|
self:T3(n)
|
|
self:T3(o)
|
|
self:T3(p)
|
|
self:T3(q)
|
|
self:T3(r)
|
|
self:T3(s)
|
|
|
|
-- t a b c d e f g h i j k l m n o p q r s
|
|
data=data..string.format("%.2f,%.2f,%.2f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f\n",
|
|
t, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s)
|
|
|
|
end
|
|
|
|
-- Save file.
|
|
_savefile(filename, data)
|
|
|
|
-- Clear data.
|
|
playerData.data={}
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- RADIO MENU Functions
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- Add menu commands for player.
|
|
-- @param #FMD self
|
|
-- @param #string _unitName Name of player unit.
|
|
function FMD:_AddF10Commands(_unitName)
|
|
self:F(_unitName)
|
|
|
|
-- Get player unit and name.
|
|
local _unit, playername = self:_GetPlayerUnitAndName(_unitName)
|
|
|
|
-- Check for player unit.
|
|
if _unit and playername then
|
|
|
|
-- Get group and ID.
|
|
local group=_unit:GetGroup()
|
|
local gid=group:GetID()
|
|
|
|
if group and gid then
|
|
|
|
if not self.menuadded[gid] then
|
|
|
|
-- Enable switch so we don't do this twice.
|
|
self.menuadded[gid]=true
|
|
|
|
-- Set menu root path.
|
|
local _rootPath=nil
|
|
if FMD.MenuF10Root then
|
|
------------------------
|
|
-- MISSON LEVEL MENUE --
|
|
------------------------
|
|
|
|
-- F10/FMD/...
|
|
_rootPath=FMD.MenuF10Root
|
|
|
|
else
|
|
------------------------
|
|
-- GROUP LEVEL MENUES --
|
|
------------------------
|
|
|
|
-- Main F10 menu: F10/FMD/
|
|
if FMD.MenuF10[gid]==nil then
|
|
FMD.MenuF10[gid]=missionCommands.addSubMenuForGroup(gid, "FMD")
|
|
end
|
|
|
|
|
|
-- F10/FMD/...
|
|
_rootPath=FMD.MenuF10[gid]
|
|
|
|
end
|
|
|
|
--------------------------------
|
|
-- F10/F<X> FMD/F1 Time Interval
|
|
--------------------------------
|
|
local _timePath=missionCommands.addSubMenuForGroup(gid, "Time Interval", _rootPath)
|
|
-- F10/FMD/F1 Time Interval/
|
|
missionCommands.addCommandForGroup(gid, "Delta t=0.01 s", _timePath, self._SetTimeInterval, self, _unitName, 0.01)
|
|
missionCommands.addCommandForGroup(gid, "Delta t=0.1 s", _timePath, self._SetTimeInterval, self, _unitName, 0.1)
|
|
missionCommands.addCommandForGroup(gid, "Delta t=1.0 s", _timePath, self._SetTimeInterval, self, _unitName, 1.0)
|
|
missionCommands.addCommandForGroup(gid, "Delta t=10 s", _timePath, self._SetTimeInterval, self, _unitName, 10.0)
|
|
missionCommands.addCommandForGroup(gid, "Delta t=30 s", _timePath, self._SetTimeInterval, self, _unitName, 30.0)
|
|
missionCommands.addCommandForGroup(gid, "Delta t=60 s", _timePath, self._SetTimeInterval, self, _unitName, 60.0)
|
|
|
|
local _durPath=missionCommands.addSubMenuForGroup(gid, "Rec Duration", _rootPath)
|
|
-- F10/FMD/F1 Rec Duration/
|
|
missionCommands.addCommandForGroup(gid, "T=10 s", _timePath, self._SetRecDuration, self, _unitName, 10)
|
|
missionCommands.addCommandForGroup(gid, "T=30 s", _timePath, self._SetRecDuration, self, _unitName, 30)
|
|
missionCommands.addCommandForGroup(gid, "T=60 s", _timePath, self._SetRecDuration, self, _unitName, 60)
|
|
missionCommands.addCommandForGroup(gid, "T=5 min", _timePath, self._SetRecDuration, self, _unitName, 5*60)
|
|
missionCommands.addCommandForGroup(gid, "T=10 min", _timePath, self._SetRecDuration, self, _unitName, 10*60)
|
|
|
|
--------------------------------
|
|
-- F10/F<X> FMD/
|
|
--------------------------------
|
|
|
|
-- F10/FMD/Start Recording
|
|
missionCommands.addCommandForGroup(gid, "Start Recording", _rootPath, self._StartRecording, self, _unitName) -- F1
|
|
missionCommands.addCommandForGroup(gid, "Stop Recording", _rootPath, self._StopRecording, self, _unitName) -- F2
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--- Set recording duration.
|
|
-- @param #FMD self
|
|
-- @param #string _unitName Name of player unit.
|
|
-- @param #number rd Recording duration in sec.
|
|
function FMD:_SetTimeInterval(_unitName, rd)
|
|
|
|
-- Get player unit and name.
|
|
local _unit, _playername = self:_GetPlayerUnitAndName(_unitName)
|
|
|
|
-- Check if we have a unit which is a player.
|
|
if _unit and _playername then
|
|
local playerData=self.players[_playername] --#FMD.PlayerData
|
|
|
|
if playerData then
|
|
|
|
-- Set dt.
|
|
playerData.rd=rd
|
|
|
|
-- Inform player.
|
|
local text=string.format("Data recording duration set to %.1f sec.", playerData.rd)
|
|
MESSAGE:New(text, 10, "FMD"):ToClient(playerData.client)
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--- Set time interval for data recording.
|
|
-- @param #FMD self
|
|
-- @param #string _unitName Name of player unit.
|
|
-- @param #number dt Time interval in seconds.
|
|
function FMD:_SetTimeInterval(_unitName, dt)
|
|
|
|
-- Get player unit and name.
|
|
local _unit, _playername = self:_GetPlayerUnitAndName(_unitName)
|
|
|
|
-- Check if we have a unit which is a player.
|
|
if _unit and _playername then
|
|
local playerData=self.players[_playername] --#FMD.PlayerData
|
|
|
|
if playerData then
|
|
|
|
-- Set dt.
|
|
playerData.dt=dt
|
|
|
|
-- Inform player.
|
|
local text=string.format("Data recording time interval set to %.3f sec.", playerData.dt)
|
|
MESSAGE:New(text, 10, "FMD"):ToClient(playerData.client)
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Start data recording.
|
|
-- @param #FMD self
|
|
-- @param #string _unitName Name of player unit.
|
|
function FMD:_StartRecording(_unitName)
|
|
|
|
-- Get player unit and name.
|
|
local _unit, _playername = self:_GetPlayerUnitAndName(_unitName)
|
|
|
|
-- Check if we have a unit which is a player.
|
|
if _unit and _playername then
|
|
local playerData=self.players[_playername] --#FMD.PlayerData
|
|
|
|
if playerData then
|
|
|
|
-- Delay before recording starts.
|
|
local delay=3
|
|
|
|
-- Inform player.
|
|
local text=string.format("Data recording will be started in %d seconds with %.3f sec timestep for %.1f sec.", delay, playerData.dt, playerData.rd)
|
|
MESSAGE:New(text, 3, "FMD"):ToClient(playerData.client)
|
|
|
|
-- Start scheduler.
|
|
playerData.SID=playerData.scheduler:Schedule(nil, self._RecordData, {self, playerData}, delay, playerData.dt, 0.0)
|
|
|
|
-- Stop scheduler once.
|
|
playerData.scheduler:ScheduleOnce(playerData.rd+delay, self._StopRecording, {self,_unitName})
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
--- Stop data recording.
|
|
-- @param #FMD self
|
|
-- @param #string _unitName Name of player unit.
|
|
function FMD:_StopRecording(_unitName)
|
|
|
|
-- Get player unit and name.
|
|
local _unit, _playername = self:_GetPlayerUnitAndName(_unitName)
|
|
|
|
-- Check if we have a unit which is a player.
|
|
if _unit and _playername then
|
|
local playerData=self.players[_playername] --#FMD.PlayerData
|
|
|
|
if playerData then
|
|
|
|
local ndata=#playerData.data
|
|
|
|
-- Inform player.
|
|
local text=string.format("Data recording stopped. Data points recorded: %d", ndata)
|
|
MESSAGE:New(text, 10, "FMD"):ToClient(playerData.client)
|
|
|
|
-- No recording switch.
|
|
playerData.recording=false
|
|
|
|
-- Stop scheduler.
|
|
playerData.scheduler:Stop(playerData.SID)
|
|
|
|
-- Save data.
|
|
self:_SaveData(playerData)
|
|
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned.
|
|
-- @param #FMD self
|
|
-- @param #string _unitName Name of the player unit.
|
|
-- @return Wrapper.Unit#UNIT Unit of player or nil.
|
|
-- @return #string Name of the player or nil.
|
|
function FMD:_GetPlayerUnitAndName(_unitName)
|
|
self:F2(_unitName)
|
|
|
|
if _unitName ~= nil then
|
|
|
|
-- Get DCS unit from its name.
|
|
local DCSunit=Unit.getByName(_unitName)
|
|
|
|
if DCSunit then
|
|
|
|
local playername=DCSunit:getPlayerName()
|
|
local unit=UNIT:Find(DCSunit)
|
|
|
|
self:T2({DCSunit=DCSunit, unit=unit, playername=playername})
|
|
if DCSunit and unit and playername then
|
|
return unit, playername
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
-- Return nil if we could not find a player.
|
|
return nil,nil
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|