From 7b8db597ef70f18f27351d3f246cceb8f750c6d8 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 7 Sep 2020 00:42:29 +0200 Subject: [PATCH] Ops --- Moose Development/Moose/Ops/ArmyGroup.lua | 200 +++++++------------ Moose Development/Moose/Ops/FlightGroup.lua | 75 ++++--- Moose Development/Moose/Ops/NavyGroup.lua | 208 +++----------------- Moose Development/Moose/Ops/OpsGroup.lua | 160 ++++++++++++--- Moose Development/Moose/Ops/Squadron.lua | 2 +- 5 files changed, 283 insertions(+), 362 deletions(-) diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 8bfe636fa..5af542aaf 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -141,18 +141,6 @@ function ARMYGROUP:SetPatrolAdInfinitum(switch) return self end ---- Set default cruise speed. This is the speed a group will take by default if no speed is specified explicitly. --- @param #ARMYGROUP self --- @param #number Speed Speed in knots. Default 70% of max speed. --- @return #ARMYGROUP self -function ARMYGROUP:SetSpeedCruise(Speed) - - self.speedCruise=Speed and UTILS.KnotsToKmph(Speed) or self.speedMax*0.7 - - return self -end - - --- Get coordinate of the closest road. -- @param #ARMYGROUP self -- @return Core.Point#COORDINATE Coordinate of a road closest to the group. @@ -212,7 +200,26 @@ function ARMYGROUP:AddTaskAttackGroup(TargetGroup, WeaponExpend, WeaponType, Clo end +--- Check if the group is currently holding its positon. +-- @param #ARMYGROUP self +-- @return #boolean If true, group was ordered to hold. +function ARMYGROUP:IsHolding() + return self:Is("Holding") +end +--- Check if the group is currently cruising. +-- @param #ARMYGROUP self +-- @return #boolean If true, group cruising. +function ARMYGROUP:IsCruising() + return self:Is("Cruising") +end + +--- Check if the group is currently on a detour. +-- @param #ARMYGROUP self +-- @return #boolean If true, group is on a detour +function ARMYGROUP:IsOnDetour() + return self:Is("OnDetour") +end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Status @@ -223,10 +230,10 @@ end function ARMYGROUP:onbeforeStatus(From, Event, To) if self:IsDead() then - self:I(self.lid..string.format("Onbefore Status DEAD ==> false")) + self:T(self.lid..string.format("Onbefore Status DEAD ==> false")) return false elseif self:IsStopped() then - self:I(self.lid..string.format("Onbefore Status STOPPED ==> false")) + self:T(self.lid..string.format("Onbefore Status STOPPED ==> false")) return false end @@ -251,27 +258,36 @@ function ARMYGROUP:onafterStatus(From, Event, To) self:_CheckDetectedUnits() end - - -- Current heading and position of the carrier. - local hdg=self:GetHeading() - local pos=self:GetCoordinate() - local speed=self.group:GetVelocityKNOTS() + -- Update position etc. + self:_UpdatePosition() + -- Check if group got stuck. + self:_CheckStuck() + + if self.verbose>=1 then - -- Get number of tasks and missions. - local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks() - local nMissions=self:CountRemainingMissison() - - -- Info text. - local text=string.format("%s: Wp=%d/%d-->%d Speed=%.1f (%d) Heading=%03d ROE=%d Alarm=%d Formation=%s Tasks=%d Missions=%d", - fsmstate, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), speed, UTILS.MpsToKnots(self.speedWp or 0), hdg, self.option.ROE, self.option.Alarm, self.option.Formation, nTaskTot, nMissions) - self:I(self.lid..text) + -- Get number of tasks and missions. + local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks() + local nMissions=self:CountRemainingMissison() + + local roe=self:GetROE() + local alarm=self:GetAlarmstate() + local speed=UTILS.MpsToKnots(self.velocity) + local speedEx=UTILS.MpsToKnots(self:GetExpectedSpeed()) + local formation=self.option.Formation + + -- Info text. + local text=string.format("%s: Wp=%d/%d-->%d Speed=%.1f (%d) Heading=%03d ROE=%d Alarm=%d Formation=%s Tasks=%d Missions=%d", + fsmstate, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), speed, speedEx, self.heading, roe, alarm, formation, nTaskTot, nMissions) + self:I(self.lid..text) + + end else -- Info text. local text=string.format("State %s: Alive=%s", fsmstate, tostring(self:IsAlive())) - self:I(self.lid..text) + self:T2(self.lid..text) end @@ -305,19 +321,6 @@ function ARMYGROUP:onafterElementSpawned(From, Event, To, Element) end ---- On after "ElementDead" event. --- @param #ARMYGROUP self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param #ARMYGROUP.Element Element The group element. -function ARMYGROUP:onafterElementDead(From, Event, To, Element) - self:T(self.lid..string.format("Element dead %s.", Element.name)) - - -- Set element status. - self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD) -end - --- On after "Spawned" event. -- @param #ARMYGROUP self -- @param #string From From state. @@ -326,10 +329,8 @@ end function ARMYGROUP:onafterSpawned(From, Event, To) self:T(self.lid..string.format("Group spawned!")) - -- TODO - self.traveldist=0 - self.traveltime=timer.getAbsTime() - self.position=self:GetCoordinate() + -- Update position. + self:_UpdatePosition() if self.ai then @@ -369,9 +370,6 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation) -- Update route from this waypoint number onwards. n=n or self:GetWaypointIndexNext(self.adinfinitum) - -- Debug info. - --self:I(self.lid..string.format("FF Update route n=%d", n)) - -- Update waypoint tasks, i.e. inject WP tasks into waypoint table. self:_UpdateWaypointTasks(n) @@ -428,8 +426,6 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation) end if wp.roaddist>100 and wp.action==ENUMS.Formation.Vehicle.OnRoad then - env.info("FF Adding ON road waypoint") - --wp.roadcoord:MarkToAll("Added Road waypoint") -- Waypoint is actually off road! wp.action=ENUMS.Formation.Vehicle.OffRoad @@ -439,17 +435,8 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation) table.insert(waypoints, wproad) end - --if wp.formation==ENUMS.Formation.Vehicle.OnRoad and wp.action~=ENUMS.Formation.Vehicle.OnRoad then --and not self.formationPerma~=ENUMS.Formation.Vehicle.OnRoad then - --[[ - if wp.action==ENUMS.Formation.Vehicle.OnRoad and wp.roaddist>100 then - env.info("FF Adding ON road waypoint") - local wproad=wp.roadcoord:WaypointGround(wp.speed, ENUMS.Formation.Vehicle.OnRoad) - table.insert(waypoints, wproad) - end - ]] - -- Debug info. - self:I(string.format("WP %d %s: Speed=%d m/s, alt=%d m, Action=%s", i, wp.type, wp.speed, wp.alt, wp.action)) + self:T(string.format("WP %d %s: Speed=%d m/s, alt=%d m, Action=%s", i, wp.type, wp.speed, wp.alt, wp.action)) -- Add waypoint. table.insert(waypoints, wp) @@ -463,7 +450,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation) if #waypoints>2 then - self:I(self.lid..string.format("Updateing route: WP %d-->%d-->%d (#%d), Speed=%.1f knots, Formation=%s", + self:T(self.lid..string.format("Updateing route: WP %d-->%d-->%d (#%d), Speed=%.1f knots, Formation=%s", self.currentwp, n, #self.waypoints, #waypoints-2, UTILS.MpsToKnots(self.speedWp), tostring(self.option.Formation))) -- Route group to all defined waypoints remaining. @@ -552,31 +539,6 @@ function ARMYGROUP:onafterCruise(From, Event, To, Speed, Formation) end ---- On after "Dead" event. --- @param #ARMYGROUP self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function ARMYGROUP:onafterDead(From, Event, To) - self:I(self.lid..string.format("Group dead!")) - - -- Delete waypoints so they are re-initialized at the next spawn. - self.waypoints=nil - self.groupinitialized=false - - -- Cancel all mission. - for _,_mission in pairs(self.missionqueue) do - local mission=_mission --Ops.Auftrag#AUFTRAG - - self:MissionCancel(mission) - mission:GroupDead(self) - - end - - -- Stop - self:Stop() -end - --- On after Start event. Starts the ARMYGROUP FSM and event handlers. -- @param #ARMYGROUP self -- @param #string From From state. @@ -584,25 +546,14 @@ end -- @param #string To To state. function ARMYGROUP:onafterStop(From, Event, To) - -- Check if group is still alive. - if self:IsAlive() then - -- Destroy group. No event is generated. - self.group:Destroy(false) - end - -- Handle events: self:UnHandleEvent(EVENTS.Birth) self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.RemoveUnit) - - -- Stop check timers. - self.timerCheckZone:Stop() - self.timerQueueUpdate:Stop() - - -- Stop FSM scheduler. - self.CallScheduler:Clear() - - self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from database.") + + -- Call OPSGROUP function. + self:GetParent(self).onafterStop(self, From, Event, To) + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -662,8 +613,8 @@ function ARMYGROUP:OnEventDead(EventData) local element=self:GetElementByName(unitname) if element then - self:I(self.lid..string.format("EVENT: Element %s dead ==> dead", element.name)) - self:ElementDead(element) + self:T(self.lid..string.format("EVENT: Element %s dead ==> destroyed", element.name)) + self:ElementDestroyed(element) end end @@ -685,7 +636,7 @@ function ARMYGROUP:OnEventRemoveUnit(EventData) local element=self:GetElementByName(unitname) if element then - self:I(self.lid..string.format("EVENT: Element %s removed ==> dead", element.name)) + self:T(self.lid..string.format("EVENT: Element %s removed ==> dead", element.name)) self:ElementDead(element) end @@ -734,19 +685,9 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation else waypoint.roaddist=1000*1000 --1000 km. end - - --[[ - if waypoint.roaddist>100 and waypoint.action==ENUMS.Formation.Vehicle.OnRoad then - waypoint.formation=ENUMS.Formation.Vehicle.OnRoad - waypoint.action=ENUMS.Formation.Vehicle.OffRoad - else - waypoint.formation=waypoint.action - end - ]] - -- Debug info. - self:I(self.lid..string.format("Adding waypoint UID=%d (index=%d), Speed=%.1f knots, Dist2Road=%d m, Action=%s", waypoint.uid, wpnumber, Speed, waypoint.roaddist, waypoint.action)) + self:T(self.lid..string.format("Adding waypoint UID=%d (index=%d), Speed=%.1f knots, Dist2Road=%d m, Action=%s", waypoint.uid, wpnumber, Speed, waypoint.roaddist, waypoint.action)) -- Update route. if Updateroute==nil or Updateroute==true then @@ -836,18 +777,20 @@ function ARMYGROUP:_InitGroup() self.actype=unit:GetTypeName() -- Debug info. - local text=string.format("Initialized Army Group %s:\n", self.groupname) - text=text..string.format("Unit type = %s\n", self.actype) - text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) - text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) - text=text..string.format("Elements = %d\n", #self.elements) - text=text..string.format("Waypoints = %d\n", #self.waypoints) - text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) - text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles) - text=text..string.format("FSM state = %s\n", self:GetState()) - text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) - text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) - self:I(self.lid..text) + if self.verbose>=1 then + local text=string.format("Initialized Army Group %s:\n", self.groupname) + text=text..string.format("Unit type = %s\n", self.actype) + text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) + text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) + text=text..string.format("Elements = %d\n", #self.elements) + text=text..string.format("Waypoints = %d\n", #self.waypoints) + text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) + text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles) + text=text..string.format("FSM state = %s\n", self:GetState()) + text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) + text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) + self:I(self.lid..text) + end -- Init done. self.groupinitialized=true @@ -885,7 +828,7 @@ function ARMYGROUP:SwitchFormation(Formation, Permanently) self:__UpdateRoute(-1, nil, nil, Formation) -- Debug info. - self:I(self.lid..string.format("Switching formation to %s (permanently=%s)", self.option.Formation, tostring(Permanently))) + self:T(self.lid..string.format("Switching formation to %s (permanently=%s)", self.option.Formation, tostring(Permanently))) end @@ -897,6 +840,7 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 55d66f504..b8286043f 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -704,6 +704,21 @@ end -- Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +---- Update status. +-- @param #FLIHGTGROUP self +function FLIGHTGROUP:onbeforeStatus(From, Event, To) + + if self:IsDead() then + self:T(self.lid..string.format("Onbefore Status DEAD ==> false")) + return false + elseif self:IsStopped() then + self:T(self.lid..string.format("Onbefore Status STOPPED ==> false")) + return false + end + + return true +end + --- On after "Status" event. -- @param #FLIGHTGROUP self -- @param #string From From state. @@ -713,6 +728,9 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) -- FSM state. local fsmstate=self:GetState() + + -- Update position. + self:_UpdatePosition() --- -- Detection @@ -752,21 +770,27 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) end --- - -- Elements + -- Group --- - local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks() - local nMissions=self:CountRemainingMissison() - -- Short info. if self.verbose>=1 then + + local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks() + local nMissions=self:CountRemainingMissison() + + local text=string.format("Status %s [%d/%d]: Tasks=%d (%d,%d) Curr=%d, Missions=%s, Waypoint=%d/%d, Detected=%d, Home=%s, Destination=%s", fsmstate, #self.elements, #self.elements, nTaskTot, nTaskSched, nTaskWP, self.taskcurrent, nMissions, self.currentwp or 0, self.waypoints and #self.waypoints or 0, self.detectedunits:Count(), self.homebase and self.homebase:GetName() or "unknown", self.destbase and self.destbase:GetName() or "unknown") self:I(self.lid..text) + end - -- Element status. + --- + -- Elements + --- + if self.verbose>=2 then local text="Elements:" for i,_element in pairs(self.elements) do @@ -801,25 +825,17 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) -- Distance travelled --- - if self.verbose>=3 and self:IsAlive() and self.position then - - local time=timer.getAbsTime() - - -- Current position. - local position=self:GetCoordinate() + if self.verbose>=3 and self:IsAlive() then -- Travelled distance since last check. - local ds=self.position:Get3DDistance(position) + local ds=self.travelds -- Time interval. - local dt=time-self.traveltime + local dt=self.dTpositionUpdate -- Speed. local v=ds/dt - -- Add up travelled distance. - self.traveldist=self.traveldist+ds - -- Max fuel time remaining. local TmaxFuel=math.huge @@ -852,11 +868,7 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) -- Log outut. self:I(self.lid..string.format("Travelled ds=%.1f km dt=%.1f s ==> v=%.1f knots. Fuel left for %.1f min", self.traveldist/1000, dt, UTILS.MpsToKnots(v), TmaxFuel/60)) - - - -- Update parameters. - self.traveltime=time - self.position=position + end --- @@ -911,8 +923,6 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) end end - - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Events ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1390,10 +1400,8 @@ end function FLIGHTGROUP:onafterSpawned(From, Event, To) self:T(self.lid..string.format("Flight spawned")) - -- TODO: general routine in opsgroup - self.traveldist=0 - self.traveltime=timer.getAbsTime() - self.position=self:GetCoordinate() + -- Update position. + self:_UpdatePosition() if self.ai then @@ -1577,7 +1585,7 @@ end -- @param #string Event Event. -- @param #string To To state. function FLIGHTGROUP:onafterLandedAt(From, Event, To) - self:I(self.lid..string.format("Flight landed at")) + self:T(self.lid..string.format("Flight landed at")) end @@ -2434,6 +2442,17 @@ function FLIGHTGROUP:onafterStop(From, Event, To) --self.group:Destroy(false) end + -- Handle events: + self:UnHandleEvent(EVENTS.Birth) + self:UnHandleEvent(EVENTS.EngineStartup) + self:UnHandleEvent(EVENTS.Takeoff) + self:UnHandleEvent(EVENTS.Land) + self:UnHandleEvent(EVENTS.EngineShutdown) + self:UnHandleEvent(EVENTS.PilotDead) + self:UnHandleEvent(EVENTS.Ejection) + self:UnHandleEvent(EVENTS.Crash) + self:UnHandleEvent(EVENTS.RemoveUnit) + -- Remove flight from data base. _DATABASE.FLIGHTGROUPS[self.groupname]=nil diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 711b5e223..7ae587da8 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -290,7 +290,7 @@ function NAVYGROUP:CreateTurnIntoWind(starttime, stoptime, speed, uturn, offset) return self end if Tstop<=Tnow then - self:I(string.format("WARNING: Into wind stop time %s already over. Tnow=%s! Input rejected.", UTILS.SecondsToClock(Tstop), UTILS.SecondsToClock(Tnow))) + self:E(string.format("WARNING: Into wind stop time %s already over. Tnow=%s! Input rejected.", UTILS.SecondsToClock(Tstop), UTILS.SecondsToClock(Tnow))) return self end @@ -389,10 +389,10 @@ end function NAVYGROUP:onbeforeStatus(From, Event, To) if self:IsDead() then - self:I(self.lid..string.format("Onbefore Status DEAD ==> false")) + self:T(self.lid..string.format("Onbefore Status DEAD ==> false")) return false elseif self:IsStopped() then - self:I(self.lid..string.format("Onbefore Status STOPPED ==> false")) + self:T(self.lid..string.format("Onbefore Status STOPPED ==> false")) return false end @@ -450,7 +450,7 @@ function NAVYGROUP:onafterStatus(From, Event, To) self:_CheckTurnsIntoWind() -- Check if group got stuck. - self:_CheckStuck() + self:_CheckStuck() if self.verbose>=1 then @@ -518,19 +518,6 @@ function NAVYGROUP:onafterElementSpawned(From, Event, To, Element) end ---- On after "ElementDead" event. --- @param #NAVYGROUP self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param #NAVYGROUP.Element Element The group element. -function NAVYGROUP:onafterElementDead(From, Event, To, Element) - self:T(self.lid..string.format("Element dead %s.", Element.name)) - - -- Set element status. - self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD) -end - --- On after "Spawned" event. -- @param #NAVYGROUP self -- @param #string From From state. @@ -539,10 +526,8 @@ end function NAVYGROUP:onafterSpawned(From, Event, To) self:T(self.lid..string.format("Group spawned!")) - -- TODO - self.traveldist=0 - self.traveltime=timer.getAbsTime() - self.position=self:GetCoordinate() + -- Update position. + self:_UpdatePosition() if self.ai then @@ -571,9 +556,6 @@ function NAVYGROUP:onafterSpawned(From, Event, To) end - -- Get orientation. - self.Corientlast=self.group:GetUnit(1):GetOrientationX() - -- Update route. self:Cruise() @@ -742,7 +724,7 @@ function NAVYGROUP:onafterTurnIntoWind(From, Event, To, IntoWind) local speed=math.max(IntoWind.Speed-vwind, 2) -- Debug info. - self:I(self.lid..string.format("Steaming into wind: Heading=%03d Speed=%.1f Vwind=%.1f Vtot=%.1f knots, Tstart=%d Tstop=%d", IntoWind.Heading, speed, vwind, speed+vwind, IntoWind.Tstart, IntoWind.Tstop)) + self:T(self.lid..string.format("Steaming into wind: Heading=%03d Speed=%.1f Vwind=%.1f Vtot=%.1f knots, Tstart=%d Tstop=%d", IntoWind.Heading, speed, vwind, speed+vwind, IntoWind.Tstart, IntoWind.Tstop)) local distance=UTILS.NMToMeters(1000) @@ -840,7 +822,7 @@ function NAVYGROUP:onafterDive(From, Event, To, Depth, Speed) Depth=Depth or 50 - self:I(self.lid..string.format("Diving to %d meters", Depth)) + self:T(self.lid..string.format("Diving to %d meters", Depth)) self.depth=Depth @@ -888,35 +870,10 @@ end -- @param #string To To state. -- @param #number Distance Distance in meters where obstacle was detected. function NAVYGROUP:onafterCollisionWarning(From, Event, To, Distance) - self:I(self.lid..string.format("Iceberg ahead in %d meters!", Distance or -1)) + self:T(self.lid..string.format("Iceberg ahead in %d meters!", Distance or -1)) self.collisionwarning=true end ---- On after "Dead" event. --- @param #NAVYGROUP self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function NAVYGROUP:onafterDead(From, Event, To) - self:I(self.lid..string.format("Group dead!")) - - -- Delete waypoints so they are re-initialized at the next spawn. - self.waypoints=nil - self.groupinitialized=false - - -- Cancel all mission. - for _,_mission in pairs(self.missionqueue) do - local mission=_mission --Ops.Auftrag#AUFTRAG - - self:MissionCancel(mission) - mission:GroupDead(self) - - end - - -- Stop - self:Stop() -end - --- On after Start event. Starts the NAVYGROUP FSM and event handlers. -- @param #NAVYGROUP self -- @param #string From From state. @@ -924,25 +881,14 @@ end -- @param #string To To state. function NAVYGROUP:onafterStop(From, Event, To) - -- Check if group is still alive. - if self:IsAlive() then - -- Destroy group. No event is generated. - self.group:Destroy(false) - end - -- Handle events: self:UnHandleEvent(EVENTS.Birth) self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.RemoveUnit) - - -- Stop check timers. - self.timerCheckZone:Stop() - self.timerQueueUpdate:Stop() - - -- Stop FSM scheduler. - self.CallScheduler:Clear() - - self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from database.") + + -- Call OPSGROUP function. + self:GetParent(self).onafterStop(self, From, Event, To) + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1002,8 +948,8 @@ function NAVYGROUP:OnEventDead(EventData) local element=self:GetElementByName(unitname) if element then - self:I(self.lid..string.format("EVENT: Element %s dead ==> dead", element.name)) - self:ElementDead(element) + self:T(self.lid..string.format("EVENT: Element %s dead ==> destroyed", element.name)) + self:ElementDestroyed(element) end end @@ -1025,7 +971,7 @@ function NAVYGROUP:OnEventRemoveUnit(EventData) local element=self:GetElementByName(unitname) if element then - self:I(self.lid..string.format("EVENT: Element %s removed ==> dead", element.name)) + self:T(self.lid..string.format("EVENT: Element %s removed ==> dead", element.name)) self:ElementDead(element) end @@ -1173,18 +1119,20 @@ function NAVYGROUP:_InitGroup() self.actype=unit:GetTypeName() -- Debug info. - local text=string.format("Initialized Navy Group %s:\n", self.groupname) - text=text..string.format("Unit type = %s\n", self.actype) - text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) - text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) - text=text..string.format("Elements = %d\n", #self.elements) - text=text..string.format("Waypoints = %d\n", #self.waypoints) - text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) - text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos) - text=text..string.format("FSM state = %s\n", self:GetState()) - text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) - text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) - self:I(self.lid..text) + if self.verbose>=1 then + local text=string.format("Initialized Navy Group %s:\n", self.groupname) + text=text..string.format("Unit type = %s\n", self.actype) + text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) + text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) + text=text..string.format("Elements = %d\n", #self.elements) + text=text..string.format("Waypoints = %d\n", #self.waypoints) + text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) + text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos) + text=text..string.format("FSM state = %s\n", self:GetState()) + text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) + text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) + self:I(self.lid..text) + end -- Init done. self.groupinitialized=true @@ -1269,7 +1217,7 @@ function NAVYGROUP:_CheckFreePath(DistanceMax, dx) local los=LoS(x) -- Debug message. - self:I(self.lid..string.format("N=%d: xmin=%.1f xmax=%.1f x=%.1f d=%.3f los=%s", N, xmin, xmax, x, d, tostring(los))) + self:T2(self.lid..string.format("N=%d: xmin=%.1f xmax=%.1f x=%.1f d=%.3f los=%s", N, xmin, xmax, x, d, tostring(los))) if los and d<=eps then return x @@ -1291,68 +1239,6 @@ function NAVYGROUP:_CheckFreePath(DistanceMax, dx) return check() end ---- Check for possible collisions between two coordinates. --- @param #NAVYGROUP self --- @param Core.Point#COORDINATE coordto Coordinate to which the collision is check. --- @param Core.Point#COORDINATE coordfrom Coordinate from which the collision is check. --- @return #boolean If true, surface type ahead is not deep water. --- @return #number Max free distance in meters. -function NAVYGROUP:_CheckCollisionCoord(coordto, coordfrom) - - -- Increment in meters. - local dx=100 - - -- From coordinate. Default 500 in front of the carrier. - local d=0 - if coordfrom then - d=0 - else - d=250 - coordfrom=self:GetCoordinate():Translate(d, self:GetHeading()) - end - - -- Distance between the two coordinates. - local dmax=coordfrom:Get2DDistance(coordto) - - -- Direction. - local direction=coordfrom:HeadingTo(coordto) - - -- Scan path between the two coordinates. - local clear=true - while d<=dmax do - - -- Check point. - local cp=coordfrom:Translate(d, direction) - - -- Check if surface type is water. - if not cp:IsSurfaceTypeWater() then - - -- Debug mark points. - if self.Debug or true then - local st=cp:GetSurfaceType() - cp:MarkToAll(string.format("Collision check surface type %d", st)) - end - - -- Collision WARNING! - clear=false - break - end - - -- Increase distance. - d=d+dx - end - - local text="" - if clear then - text=string.format("Path into direction %03d° is clear for the next %.1f NM.", direction, UTILS.MetersToNM(d)) - else - text=string.format("Detected obstacle at distance %.1f NM into direction %03d°.", UTILS.MetersToNM(d), direction) - end - self:T(self.lid..text) - - return not clear, d -end - --- Check if group is turning. -- @param #NAVYGROUP self function NAVYGROUP:_CheckTurning() @@ -1365,7 +1251,7 @@ function NAVYGROUP:_CheckTurning() local vNew=self.orientX --unit:GetOrientationX() -- Last orientation from 30 seconds ago. - local vLast=self.orientXLast --self.Corientlast or vNew + local vLast=self.orientXLast -- We only need the X-Z plane. vNew.y=0 ; vLast.y=0 @@ -1373,9 +1259,6 @@ function NAVYGROUP:_CheckTurning() -- Angle between current heading and last time we checked ~30 seconds ago. local deltaLast=math.deg(math.acos(UTILS.VecDot(vNew,vLast)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vLast))) - -- Last orientation becomes new orientation - --self.Corientlast=vNew - -- Carrier is turning when its heading changed by at least two degrees since last check. local turning=math.abs(deltaLast)>=2 @@ -1399,33 +1282,6 @@ function NAVYGROUP:_CheckTurning() end ---- Check if group got stuck. --- @param #NAVYGROUP self -function NAVYGROUP:_CheckStuck() - - if self:IsHolding() then - return - end - - local holdtime=0 - if self.holdtimestamp then - holdtime=timer.getTime()-self.holdtimestamp - end - - local ExpectedSpeed=self:GetExpectedSpeed() - - local speed=self:GetVelocity() - - if speed<0.5 and ExpectedSpeed>0 then - if not self.holdtimestamp then - self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected", speed, ExpectedSpeed)) - self.holdtimestamp=timer.getTime() - end - end - -end - - --- Check queued turns into wind. -- @param #NAVYGROUP self diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 1dbfccef9..bdd2c2f17 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -54,8 +54,14 @@ -- @field #number Ndestroyed Number of destroyed units. -- -- @field Core.Point#COORDINATE coordinate Current coordinate. --- @field Core.Point#COORDINATE position Position of the group at last status check. --- @field #number traveldist Distance traveled in meters. This is a lower bound! +-- +-- @field DCS#Vec3 position Position of the group at last status check. +-- @field DCS#Vec3 positionLast Backup of last position vec to monitor changes. +-- @field #number heading Heading of the group at last status check. +-- @field #number headingLast Backup of last heading to monitor changes. +-- @field DCS#Vec3 orientX Orientation at last status check. +-- @field DCS#Vec3 orientXLast Backup of last orientation to monitor changes. +-- @field #number traveldist Distance traveled in meters. This is a lower bound. -- @field #number traveltime Time. -- -- @field Core.Astar#ASTAR Astar path finding. @@ -590,7 +596,7 @@ function OPSGROUP:Destroy(Delay) if DCSGroup then - self:I(self.lid.."Destroying group") + self:T(self.lid.."Destroying group") -- Destroy DCS group. DCSGroup:destroy() @@ -650,8 +656,9 @@ end --- Get current coordinate of the group. -- @param #OPSGROUP self +-- @param #boolean NewObject Create a new coordiante object. -- @return Core.Point#COORDINATE The coordinate (of the first unit) of the group. -function OPSGROUP:GetCoordinate() +function OPSGROUP:GetCoordinate(NewObject) local vec3=self:GetVec3() @@ -663,7 +670,11 @@ function OPSGROUP:GetCoordinate() self.coordinate.y=vec3.y self.coordinate.z=vec3.z - return self.coordinate + if NewObject then + local coord=COORDINATE:NewFromCoordinate(self.coordinate) + else + return self.coordinate + end else self:E(self.lid.."WARNING: Group is not alive. Cannot get coordinate!") end @@ -725,6 +736,41 @@ function OPSGROUP:GetHeading() return nil end +--- Get current orientation of the first unit in the group. +-- @param #OPSGROUP self +-- @return DCS#Vec3 Orientation X parallel to where the "nose" is pointing. +-- @return DCS#Vec3 Orientation Y pointing "upwards". +-- @return DCS#Vec3 Orientation Z perpendicular to the "nose". +function OPSGROUP:GetOrientation() + + if self:IsExist() then + + local unit=self:GetDCSUnit() + + if unit then + + local pos=unit:getPosition() + + return pos.x, pos.y, pos.z + end + + else + self:E(self.lid.."WARNING: Group does not exist. Cannot get orientation!") + end + + return nil +end + +--- Get current orientation of the first unit in the group. +-- @param #OPSGROUP self +-- @return DCS#Vec3 Orientation X parallel to where the "nose" is pointing. +function OPSGROUP:GetOrientationX() + + local X,Y,Z=self:GetOrientation() + + return X +end + --- Check if task description is unique. @@ -2658,7 +2704,7 @@ end -- @param #string To To state. -- @param #OPSGROUP.Element Element The flight group element. function OPSGROUP:onafterElementDestroyed(From, Event, To, Element) - self:I(self.lid..string.format("Element destroyed %s", Element.name)) + self:T(self.lid..string.format("Element destroyed %s", Element.name)) -- Cancel all missions. for _,_mission in pairs(self.missionqueue) do @@ -2683,7 +2729,7 @@ end -- @param #string To To state. -- @param #OPSGROUP.Element Element The flight group element. function OPSGROUP:onafterElementDead(From, Event, To, Element) - self:I(self.lid..string.format("Element dead %s", Element.name)) + self:T(self.lid..string.format("Element dead %s", Element.name)) -- Set element status. self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD) @@ -2695,7 +2741,7 @@ end -- @param #string Event Event. -- @param #string To To state. function OPSGROUP:onafterDead(From, Event, To) - self:T(self.lid..string.format("Flight dead!")) + self:T(self.lid..string.format("Group dead!")) -- Delete waypoints so they are re-initialized at the next spawn. self.waypoints=nil @@ -2710,8 +2756,8 @@ function OPSGROUP:onafterDead(From, Event, To) end - -- Stop - self:Stop() + -- Stop in a sec. + self:__Stop(-1) end --- On after "Stop" event. @@ -2720,17 +2766,6 @@ end -- @param #string Event Event. -- @param #string To To state. function OPSGROUP:onafterStop(From, Event, To) - - -- Handle events: - self:UnHandleEvent(EVENTS.Birth) - self:UnHandleEvent(EVENTS.EngineStartup) - self:UnHandleEvent(EVENTS.Takeoff) - self:UnHandleEvent(EVENTS.Land) - self:UnHandleEvent(EVENTS.EngineShutdown) - self:UnHandleEvent(EVENTS.PilotDead) - self:UnHandleEvent(EVENTS.Ejection) - self:UnHandleEvent(EVENTS.Crash) - self:UnHandleEvent(EVENTS.RemoveUnit) -- Stop check timers. self.timerCheckZone:Stop() @@ -3915,26 +3950,43 @@ end --- Check if all elements of the group have the same status (or are dead). -- @param #OPSGROUP self --- @param #string unitname Name of unit. +-- @return #OPSGROUP self function OPSGROUP:_UpdatePosition() if self:IsAlive() then - self.positionLast=self.position or self:GetCoordinate() + -- Backup last state to monitor differences. + self.positionLast=self.position or self:GetVec3() self.headingLast=self.heading or self:GetHeading() - self.orientXLast=self.orientX or self.group:GetUnit(1):GetOrientationX() + self.orientXLast=self.orientX or self:GetOrientationX() self.velocityLast=self.velocity or self.group:GetVelocityMPS() - self.position=self:GetCoordinate() + -- Current state. + self.position=self:GetVec3() self.heading=self:GetHeading() - self.orientX=self.group:GetUnit(1):GetOrientationX() - self.velocity=self.group:GetVelocityMPS() + self.orientX=self:GetOrientationX() + self.velocity=self:GetVelocity() - self.dTpositionUpdate=self.TpositionUpdate and self.TpositionUpdate-timer.getAbsTime() or 0 - self.TpositionUpdate=timer.getAbsTime() + -- Update time. + local Tnow=timer.getTime() + self.dTpositionUpdate=self.TpositionUpdate and Tnow-self.TpositionUpdate or 0 + self.TpositionUpdate=Tnow + + if not self.traveldist then + self.traveldist=0 + end + + self.travelds=UTILS.VecNorm(UTILS.VecSubstract(self.position, self.positionLast)) + + -- Add up travelled distance. + + self.traveldist=self.traveldist+self.travelds + + env.info(string.format("FF Traveled %.1f m", self.traveldist)) end + return self end --- Check if all elements of the group have the same status (or are dead). @@ -4483,6 +4535,56 @@ function OPSGROUP:_MissileCategoryName(categorynumber) return cat end +--- Check if group got stuck. +-- @param #OPSGROUP self +function OPSGROUP:_CheckStuck() + + -- Holding means we are not stuck. + if self:IsHolding() then + return + end + + -- Current time. + local Tnow=timer.getTime() + + -- Expected speed in m/s. + local ExpectedSpeed=self:GetExpectedSpeed() + + -- Current speed in m/s. + local speed=self:GetVelocity() + + -- Check speed. + if speed<0.5 then + + if ExpectedSpeed>0 and not self.stuckTimestamp then + self:T2(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected", speed, ExpectedSpeed)) + self.stuckTimestamp=Tnow + self.stuckVec3=self:GetVec3() + end + + else + -- Moving (again). + self.stuckTimestamp=nil + end + + -- Somehow we are not moving... + if self.stuckTimestamp then + + -- Time we are holding. + local holdtime=Tnow-self.stuckTimestamp + + if holdtime>=5*60 then + + self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime)) + + --TODO: Stuck event! + + end + + end + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index a9a7ce6ce..d7a5ca114 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -62,7 +62,7 @@ -- @field #SQUADRON SQUADRON = { ClassName = "SQUADRON", - verbose = 3, + verbose = 0, lid = nil, name = nil, templatename = nil,