From ac337b845910ae058b1e28473f3a84231d3c3567 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 28 Oct 2020 09:06:22 +0100 Subject: [PATCH] Ops - Laser --- Moose Development/Moose/Core/Timer.lua | 19 +- Moose Development/Moose/Ops/OpsGroup.lua | 372 +++++++++++++---------- 2 files changed, 232 insertions(+), 159 deletions(-) diff --git a/Moose Development/Moose/Core/Timer.lua b/Moose Development/Moose/Core/Timer.lua index 26f809c1d..258996d68 100644 --- a/Moose Development/Moose/Core/Timer.lua +++ b/Moose Development/Moose/Core/Timer.lua @@ -27,6 +27,7 @@ -- @field #number dT Time interval between function calls in seconds. -- @field #number ncalls Counter of function calls. -- @field #number ncallsMax Max number of function calls. If reached, timer is stopped. +-- @field #boolean isrunning If `true`, timer is running. Else it was not started yet or was stopped. -- @extends Core.Base#BASE --- *Better three hours too soon than a minute too late.* - William Shakespeare @@ -111,7 +112,7 @@ _TIMERID=0 --- TIMER class version. -- @field #string version -TIMER.version="0.1.0" +TIMER.version="0.1.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -143,6 +144,9 @@ function TIMER:New(Function, ...) -- Number of function calls. self.ncalls=0 + -- Not running yet. + self.isrunning=false + -- Increase counter _TIMERID=_TIMERID+1 @@ -186,6 +190,9 @@ function TIMER:Start(Tstart, dT, Duration) -- Set log id. self.lid=string.format("TIMER UID=%d/%d | ", self.uid, self.tid) + -- Is now running. + self.isrunning=true + -- Debug info. self:T(self.lid..string.format("Starting Timer in %.3f sec, dT=%s, Tstop=%s", self.Tstart-Tnow, tostring(self.dT), tostring(self.Tstop))) @@ -210,6 +217,9 @@ function TIMER:Stop(Delay) self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!", self.ncalls)) timer.removeFunction(self.tid) + -- Not running any more. + self.isrunning=false + -- Remove DB entry. --_TIMERDB[self.uid]=nil @@ -229,6 +239,13 @@ function TIMER:SetMaxFunctionCalls(Nmax) return self end +--- Check if the timer has been started and was not stopped. +-- @param #TIMER self +-- @return #boolean If `true`, the timer is running. +function TIMER:IsRunning() + return self.isrunning +end + --- Call timer function. -- @param #TIMER self -- @param #number time DCS model time in seconds. diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 3ead74115..97ef840df 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -275,6 +275,8 @@ OPSGROUP.TaskType={ -- @field Core.Point#COORDINATE Coordinate where the spot is pointing. -- @field #number TargetType Type of target: 0=coordinate, 1=static, 2=unit, 3=group. -- @field #boolean On If true, the laser is on. +-- @field #boolean Paused If true, laser is paused. +-- @field #boolean lostLOS If true, laser lost LOS. -- @field #OPSGROUP.Element element The element of the group that is lasing. -- @field DCS#Vec3 vec3 The 3D positon vector of the laser (and IR) spot. -- @field DCS#Vec3 offset Local offset of the laser source. @@ -415,6 +417,7 @@ function OPSGROUP:New(Group) self:AddTransition("*", "LaserOff", "*") -- Turn laser off. self:AddTransition("*", "LaserCode", "*") -- Switch laser code. self:AddTransition("*", "LaserPause", "*") -- Turn laser off temporarily. + self:AddTransition("*", "LaserResume", "*") -- Turn laser back on again if it was paused. self:AddTransition("*", "LaserLostLOS", "*") -- Lasing element lost line of sight. self:AddTransition("*", "LaserGotLOS", "*") -- Lasing element got line of sight. @@ -671,6 +674,76 @@ function OPSGROUP:GetThreat(ThreatLevelMin, ThreatLevelMax) return threat, level end +--- Get highest threat. +-- @param #OPSGROUP self +-- @return Wrapper.Unit#UNIT The highest threat unit. +-- @return #number Threat level of the unit. +function OPSGROUP:GetHighestThreat() + + local threat=nil + local levelmax=-1 + for _,_unit in pairs(self.detectedunits:GetSet()) do + local unit=_unit --Wrapper.Unit#UNIT + + local threatlevel=unit:GetThreatLevel() + + if threatlevel>levelmax then + threat=unit + levelmax=threatlevel + end + + end + + return threat, levelmax +end + +--- Check if an element of the group has line of sight to a coordinate. +-- @param #OPSGROUP self +-- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. +-- @param #OPSGROUP.Element Element The (optinal) element. If not given, all elements are checked. +-- @param DCS#Vec3 OffsetElement Offset vector of the element. +-- @param DCS#Vec3 OffsetCoordinate Offset vector of the coordinate. +-- @return #boolean If `true`, there is line of sight to the specified coordinate. +function OPSGROUP:HasLoS(Coordinate, Element, OffsetElement, OffsetCoordinate) + + -- Target vector. + local Vec3=Coordinate:GetVec3() + + -- Optional offset. + if OffsetCoordinate then + Vec3=UTILS.VecAdd(Vec3, OffsetCoordinate) + end + + --- Function to check LoS for an element of the group. + local function checklos(element) + local vec3=element.unit:GetVec3() + if OffsetElement then + vec3=UTILS.VecAdd(vec3, OffsetElement) + end + local _los=land.isVisible(vec3, Vec3) + --self:I({los=_los, source=vec3, target=Vec3}) + return _los + end + + if Element then + local los=checklos(Element) + return los + else + + for _,element in pairs(self.elements) do + -- Get LoS of this element. + local los=checklos(element) + if los then + return true + end + end + + return false + end + + return nil +end + --- Get MOOSE GROUP object. -- @param #OPSGROUP self -- @return Wrapper.Group#GROUP Moose group object. @@ -3085,7 +3158,7 @@ function OPSGROUP:onbeforeLaserOn(From, Event, To, Target) else -- No target specified. - --TODO: error message + self:E(self.lid.."ERROR: No target provided for LASER!") return false end @@ -3095,10 +3168,7 @@ function OPSGROUP:onbeforeLaserOn(From, Event, To, Target) if element then -- Set element. - self.spot.element=element - - -- Vec3. - self.spot.vec3=self.spot.Coordinate:GetVec3() + self.spot.element=element -- Height offset. No offset for aircraft. We take the height for ground or naval. local offsetY=0 @@ -3113,13 +3183,15 @@ function OPSGROUP:onbeforeLaserOn(From, Event, To, Target) if self.spot.CheckLOS then -- Check LOS. - self.spot.LOS=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) + local los=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) + + --self:I({los=los, coord=self.spot.Coordinate, offset=self.spot.offset}) - if self.spot.LOS then + if los then self:LaserGotLOS() else -- Try to switch laser on again in 10 sec. - self:T(self.lid.."LASER got no LOS currently. Trying to switch the laser on again in 10 sec") + self:I(self.lid.."LASER got no LOS currently. Trying to switch the laser on again in 10 sec") self:__LaserOn(-10, Target) return false end @@ -3143,7 +3215,9 @@ end function OPSGROUP:onafterLaserOn(From, Event, To, Target) -- Start timer that calls the update twice per sec by default. - self.spot.timer:Start(nil, self.spot.dt) + if not self.spot.timer:IsRunning() then + self.spot.timer:Start(nil, self.spot.dt) + end -- Get DCS unit. local DCSunit=self.spot.element.unit:GetDCSObject() @@ -3156,6 +3230,9 @@ function OPSGROUP:onafterLaserOn(From, Event, To, Target) -- Laser is on. self.spot.On=true + + -- No paused in case it was. + self.spot.Paused=false -- Debug message. self:T(self.lid.."Switching LASER on") @@ -3168,7 +3245,7 @@ end -- @param #string Event Event. -- @param #string To To state. function OPSGROUP:onbeforeLaserOff(From, Event, To) - return self.spot.On + return self.spot.On or self.spot.Paused end --- On after "LaserOff" event. @@ -3182,20 +3259,26 @@ function OPSGROUP:onafterLaserOff(From, Event, To) self:T(self.lid.."Switching LASER off") -- "Destroy" the laser beam. - self.spot.Laser:destroy() - self.spot.IR:destroy() + if self.spot.On then + self.spot.Laser:destroy() + self.spot.IR:destroy() + + -- Set to nil. + self.spot.Laser=nil + self.spot.IR=nil + end -- Stop update timer. self.spot.timer:Stop() - -- Set to nil. - self.spot.Laser=nil - self.spot.IR=nil + -- No target unit. self.spot.TargetUnit=nil -- Laser is off. self.spot.On=false + -- Not paused if it was. + self.spot.Paused=false end --- On after "LaserPause" event. @@ -3219,6 +3302,52 @@ function OPSGROUP:onafterLaserPause(From, Event, To) -- Laser is off. self.spot.On=false + -- Laser is paused. + self.spot.Paused=true + +end + +--- On before "LaserResume" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function OPSGROUP:onbeforeLaserResume(From, Event, To) + return self.spot.Paused +end + +--- On after "LaserResume" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function OPSGROUP:onafterLaserResume(From, Event, To) + + -- Debug info. + self:I(self.lid.."Resuming LASER") + + -- Unset paused. + self.spot.Paused=false + + -- Set target. + local target=nil + if self.spot.TargetType==0 then + target=self.spot.Coordinate + elseif self.spot.TargetType==1 or self.spot.TargetType==2 then + target=self.spot.TargetUnit + elseif self.spot.TargetType==3 then + target=self.spot.TargetGroup + end + + -- Switch laser back on. + if target then + + -- Debug message. + self:I(self.lid.."Switching LASER on again at target ".. target:GetName()) + + self:LaserOn(target) + end + end --- On after "LaserCode" event. Changes the LASER code. @@ -3253,8 +3382,11 @@ end -- @param #string To To state. function OPSGROUP:onafterLaserLostLOS(From, Event, To) - -- Lost line of sight. + -- No of sight. self.spot.LOS=false + + -- Lost line of sight. + self.spot.lostLOS=true if self.spot.On then @@ -3272,25 +3404,24 @@ end -- @param #string To To state. function OPSGROUP:onafterLaserGotLOS(From, Event, To) - -- Lost line of sight. + -- Has line of sight. self.spot.LOS=true - - if not self.spot.On then - local target=nil + env.info("FF Laser Got LOS") + + if self.spot.lostLOS then + + -- Did not loose LOS anymore. + self.spot.lostLOS=false - if self.spot.TargetType==0 then - target=self.spot.Coordinate - elseif self.spot.TargetType==1 or self.spot.TargetType==2 then - target=self.spot.TargetUnit - elseif self.spot.TargetType==3 then - target=self.spot.TargetGroup + env.info("FF had lost LOS and regained it") + + -- Resume laser if currently paused. + if self.spot.Paused then + env.info("FF laser was paused ==> resume") + self:LaserResume() end - -- Switch laser back on. - if target then - self:__LaserOn(-1, target) - end end end @@ -3327,7 +3458,7 @@ function OPSGROUP:SetLaserTarget(Target) local size,x,y,z=self.spot.TargetUnit:GetObjectSize() if y then - self.spot.offsetTarget={x=0, y=y/2, z=0} + self.spot.offsetTarget={x=0, y=y*0.75, z=0} else self.spot.offsetTarget={x=0, 2, z=0} end @@ -3347,161 +3478,85 @@ function OPSGROUP:SetLaserTarget(Target) self:E(self.lid.."ERROR: LASER target should be a POSITIONABLE (GROUP, UNIT or STATIC) or a COORDINATE object!") return end - - + -- Set vec3 and account for target offset. self.spot.vec3=UTILS.VecAdd(Target:GetVec3(), self.spot.offsetTarget) -- Set coordinate. - self.spot.Coordinate:UpdateFromVec3(self.spot.vec3) - + self.spot.Coordinate:UpdateFromVec3(self.spot.vec3) end end ---- Get highest threat. --- @param #OPSGROUP self --- @return Wrapper.Unit#UNIT The highest threat unit. --- @return #number Threat level of the unit. -function OPSGROUP:GetHighestThreat() - - local threat=nil - local levelmax=-1 - for _,_unit in pairs(self.detectedunits:GetSet()) do - local unit=_unit --Wrapper.Unit#UNIT - - local threatlevel=unit:GetThreatLevel() - - if threatlevel>levelmax then - threat=unit - levelmax=threatlevel - end - - end - - return threat, levelmax -end - ---- Check if an element of the group has line of sight to a coordinate. --- @param #OPSGROUP self --- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. --- @param #OPSGROUP.Element Element The (optinal) element. If not given, all elements are checked. --- @param DCS#Vec3 OffsetElement Offset vector of the element. --- @param DCS#Vec3 OffsetCoordinate Offset vector of the coordinate. --- @return #boolean If `true`, there is line of sight to the specified coordinate. -function OPSGROUP:HasLoS(Coordinate, Element, OffsetElement, OffsetCoordinate) - - -- Target vector. - local Vec3=Coordinate:GetVec3() - - -- Optional offset. - if OffsetCoordinate then - Vec3=UTILS.VecAdd(vec3, OffsetCoordinate) - end - - --- Function to check LoS for an element of the group. - local function checklos(element) - local vec3=element.unit:GetVec3() - if OffsetElement then - vec3=UTILS.VecAdd(vec3, OffsetElement) - end - local _los=land.isVisible(vec3, Vec3) - --self:I({los=_los, source=vec3, target=Vec3}) - return _los - end - - if Element then - local los=checklos(Element) - return los - else - - for _,element in pairs(self.elements) do - -- Get LoS of this element. - local los=checklos(element) - if los then - return true - end - end - - return false - end - - return nil -end - --- Update laser point. -- @param #OPSGROUP self function OPSGROUP:_UpdateLaser() - -- Firstly, check if laser is on. - if self.spot.On then + -- Check if we have a POSITIONABLE to lase. + if self.spot.TargetUnit then --- - -- LASER is ON + -- Lasing a possibly moving target --- - -- Check if we have a POSITIONABLE to lase. - if self.spot.TargetUnit then - - --- - -- Lasing a possibly moving target - --- - - if self.spot.TargetUnit:IsAlive() then + if self.spot.TargetUnit:IsAlive() then - -- Get current target position. - local vec3=self.spot.TargetUnit:GetVec3() - - -- Add target offset. - vec3=UTILS.VecAdd(vec3, self.spot.offsetTarget) - - -- Calculate distance - local dist=UTILS.VecDist3D(vec3, self.spot.vec3) - - -- Update laser if target moved more than one meter. - if dist>1 then - - -- Store current position. - self.spot.vec3=vec3 - - -- Update beam coordinate. - self.spot.Coordinate:UpdateFromVec3(vec3) - - -- Set the new laser target point. + -- Get current target position. + local vec3=self.spot.TargetUnit:GetVec3() + + -- Add target offset. + vec3=UTILS.VecAdd(vec3, self.spot.offsetTarget) + + -- Calculate distance + local dist=UTILS.VecDist3D(vec3, self.spot.vec3) + + -- Store current position. + self.spot.vec3=vec3 + + -- Update beam coordinate. + self.spot.Coordinate:UpdateFromVec3(vec3) + + -- Update laser if target moved more than one meter. + if dist>1 then + + -- If the laser is ON, set the new laser target point. + if self.spot.On then self.spot.Laser:setPoint(vec3) if self.spot.IRon then self.spot.IR:setPoint(vec3) end - end - else + end - if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive() then - - -- Get first alive unit in the group. - local unit=self.spot.TargetGroup:GetHighestThreat() - - if unit then - self:I(self.lid..string.format("Switching to target unit %s in the group", unit:GetName())) - self.spot.TargetUnit=unit - else - -- Switch laser off. - self:I(self.lid.."Target is not alive any more ==> switching LASER off") - self:LaserOff() - return - end + else + + if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive() then + + -- Get first alive unit in the group. + local unit=self.spot.TargetGroup:GetHighestThreat() + if unit then + self:I(self.lid..string.format("Switching to target unit %s in the group", unit:GetName())) + self.spot.TargetUnit=unit + -- We update the laser position in the next update cycle and then check the LOS. + return else - -- Switch laser off. self:I(self.lid.."Target is not alive any more ==> switching LASER off") self:LaserOff() - return + return end - end - end + else + + -- Switch laser off. + self:I(self.lid.."Target is not alive any more ==> switching LASER off") + self:LaserOff() + return + end + + end end -- Check LOS. @@ -3510,17 +3565,18 @@ function OPSGROUP:_UpdateLaser() -- Check current LOS. local los=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) - --env.info(string.format("FF check LOS=%s", tostring(los))) + env.info(string.format("FF check LOS current=%s previous=%s", tostring(los), tostring(self.spot.LOS))) if los then -- Got LOS - if not self.spot.LOS then + if self.spot.lostLOS then + --self:I({los=self.spot.LOS, coord=self.spot.Coordinate, offset=self.spot.offset}) self:LaserGotLOS() end else -- No LOS currently - if self.spot.LOS then + if not self.spot.lostLOS then self:LaserLostLOS() end