--- **Tasking** -- This module contains the TASK class. -- -- === -- -- -- === -- -- ### Authors: FlightControl - Design and Programming -- -- @module Task --- @type TASK -- @field Core.Scheduler#SCHEDULER TaskScheduler -- @field Tasking.Mission#MISSION Mission -- @field Core.Set#SET_GROUP SetGroup The Set of Groups assigned to the Task -- @field Core.Fsm#FSM_PROCESS FsmTemplate -- @field Tasking.Mission#MISSION Mission -- @field Tasking.CommandCenter#COMMANDCENTER CommandCenter -- @extends Core.Fsm#FSM_TASK --- -- # TASK class, extends @{Base#BASE} -- -- ## The TASK class implements the methods for task orchestration within MOOSE. -- -- The class provides a couple of methods to: -- -- * @{#TASK.AssignToGroup}():Assign a task to a group (of players). -- * @{#TASK.AddProcess}():Add a @{Process} to a task. -- * @{#TASK.RemoveProcesses}():Remove a running @{Process} from a running task. -- * @{#TASK.SetStateMachine}():Set a @{Fsm} to a task. -- * @{#TASK.RemoveStateMachine}():Remove @{Fsm} from a task. -- * @{#TASK.HasStateMachine}():Enquire if the task has a @{Fsm} -- * @{#TASK.AssignToUnit}(): Assign a task to a unit. (Needs to be implemented in the derived classes from @{#TASK}. -- * @{#TASK.UnAssignFromUnit}(): Unassign the task from a unit. -- * @{#TASK.SetTimeOut}(): Set timer in seconds before task gets cancelled if not assigned. -- -- ## 1.2) Set and enquire task status (beyond the task state machine processing). -- -- A task needs to implement as a minimum the following task states: -- -- * **Success**: Expresses the successful execution and finalization of the task. -- * **Failed**: Expresses the failure of a task. -- * **Planned**: Expresses that the task is created, but not yet in execution and is not assigned yet. -- * **Assigned**: Expresses that the task is assigned to a Group of players, and that the task is in execution mode. -- -- A task may also implement the following task states: -- -- * **Rejected**: Expresses that the task is rejected by a player, who was requested to accept the task. -- * **Cancelled**: Expresses that the task is cancelled by HQ or through a logical situation where a cancellation of the task is required. -- -- A task can implement more statusses than the ones outlined above. Please consult the documentation of the specific tasks to understand the different status modelled. -- -- The status of tasks can be set by the methods **State** followed by the task status. An example is `StateAssigned()`. -- The status of tasks can be enquired by the methods **IsState** followed by the task status name. An example is `if IsStateAssigned() then`. -- -- ## 1.3) Add scoring when reaching a certain task status: -- -- Upon reaching a certain task status in a task, additional scoring can be given. If the Mission has a scoring system attached, the scores will be added to the mission scoring. -- Use the method @{#TASK.AddScore}() to add scores when a status is reached. -- -- ## 1.4) Task briefing: -- -- A task briefing can be given that is shown to the player when he is assigned to the task. -- -- @field #TASK TASK -- TASK = { ClassName = "TASK", TaskScheduler = nil, ProcessClasses = {}, -- The container of the Process classes that will be used to create and assign new processes for the task to ProcessUnits. Processes = {}, -- The container of actual process objects instantiated and assigned to ProcessUnits. Players = nil, Scores = {}, Menu = {}, SetGroup = nil, FsmTemplate = nil, Mission = nil, CommandCenter = nil, TimeOut = 0, } --- FSM PlayerAborted event handler prototype for TASK. -- @function [parent=#TASK] OnAfterPlayerAborted -- @param #TASK self -- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he went back to spectators or left the mission. -- @param #string PlayerName The name of the Player. --- FSM PlayerCrashed event handler prototype for TASK. -- @function [parent=#TASK] OnAfterPlayerCrashed -- @param #TASK self -- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he crashed in the mission. -- @param #string PlayerName The name of the Player. --- FSM PlayerDead event handler prototype for TASK. -- @function [parent=#TASK] OnAfterPlayerDead -- @param #TASK self -- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he died in the mission. -- @param #string PlayerName The name of the Player. --- FSM Fail synchronous event function for TASK. -- Use this event to Fail the Task. -- @function [parent=#TASK] Fail -- @param #TASK self --- FSM Fail asynchronous event function for TASK. -- Use this event to Fail the Task. -- @function [parent=#TASK] __Fail -- @param #TASK self --- FSM Abort synchronous event function for TASK. -- Use this event to Abort the Task. -- @function [parent=#TASK] Abort -- @param #TASK self --- FSM Abort asynchronous event function for TASK. -- Use this event to Abort the Task. -- @function [parent=#TASK] __Abort -- @param #TASK self --- FSM Success synchronous event function for TASK. -- Use this event to make the Task a Success. -- @function [parent=#TASK] Success -- @param #TASK self --- FSM Success asynchronous event function for TASK. -- Use this event to make the Task a Success. -- @function [parent=#TASK] __Success -- @param #TASK self --- FSM Cancel synchronous event function for TASK. -- Use this event to Cancel the Task. -- @function [parent=#TASK] Cancel -- @param #TASK self --- FSM Cancel asynchronous event function for TASK. -- Use this event to Cancel the Task. -- @function [parent=#TASK] __Cancel -- @param #TASK self --- FSM Replan synchronous event function for TASK. -- Use this event to Replan the Task. -- @function [parent=#TASK] Replan -- @param #TASK self --- FSM Replan asynchronous event function for TASK. -- Use this event to Replan the Task. -- @function [parent=#TASK] __Replan -- @param #TASK self --- Instantiates a new TASK. Should never be used. Interface Class. -- @param #TASK self -- @param Tasking.Mission#MISSION Mission The mission wherein the Task is registered. -- @param Core.Set#SET_GROUP SetGroupAssign The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task -- @param #string TaskType The type of the Task -- @return #TASK self function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) local self = BASE:Inherit( self, FSM_TASK:New() ) -- Core.Fsm#FSM_TASK self:SetStartState( "Planned" ) self:AddTransition( "Planned", "Assign", "Assigned" ) self:AddTransition( "Assigned", "AssignUnit", "Assigned" ) self:AddTransition( "Assigned", "Success", "Success" ) self:AddTransition( "Assigned", "Fail", "Failed" ) self:AddTransition( "Assigned", "Abort", "Aborted" ) self:AddTransition( "Assigned", "Cancel", "Cancelled" ) self:AddTransition( "*", "PlayerCrashed", "*" ) self:AddTransition( "*", "PlayerAborted", "*" ) self:AddTransition( "*", "PlayerDead", "*" ) self:AddTransition( { "Failed", "Aborted", "Cancelled" }, "Replan", "Planned" ) self:AddTransition( "*", "TimeOut", "Cancelled" ) self:E( "New TASK " .. TaskName ) self.Processes = {} self.Fsm = {} self.Mission = Mission self.CommandCenter = Mission:GetCommandCenter() self.SetGroup = SetGroupAssign self:SetType( TaskType ) self:SetName( TaskName ) self:SetID( Mission:GetNextTaskID( self ) ) -- The Mission orchestrates the task sequences .. self.TaskBriefing = "You are invited for the task: " .. self.TaskName .. "." self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() self.TaskInfo = {} return self end --- Get the Task FSM Process Template -- @param #TASK self -- @return Core.Fsm#FSM_PROCESS function TASK:GetUnitProcess( TaskUnit ) if TaskUnit then return self:GetStateMachine( TaskUnit ) else return self.FsmTemplate end end --- Sets the Task FSM Process Template -- @param #TASK self -- @param Core.Fsm#FSM_PROCESS function TASK:SetUnitProcess( FsmTemplate ) self.FsmTemplate = FsmTemplate end --- Add a PlayerUnit to join the Task. -- For each Group within the Task, the Unit is checked if it can join the Task. -- If the Unit was not part of the Task, false is returned. -- If the Unit is part of the Task, true is returned. -- @param #TASK self -- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. -- @param Wrapper.Group#GROUP PlayerGroup The GROUP of the player joining the Mission. -- @return #boolean true if Unit is part of the Task. function TASK:JoinUnit( PlayerUnit, PlayerGroup ) self:F( { PlayerUnit = PlayerUnit, PlayerGroup = PlayerGroup } ) local PlayerUnitAdded = false local PlayerGroups = self:GetGroups() -- Is the PlayerGroup part of the PlayerGroups? if PlayerGroups:IsIncludeObject( PlayerGroup ) then -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is added to the Task. -- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader. if self:IsStatePlanned() or self:IsStateReplanned() then self:SetMenuForGroup( PlayerGroup ) --self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) end if self:IsStateAssigned() then local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) self:E( { IsAssignedToGroup = IsAssignedToGroup } ) if IsAssignedToGroup then self:AssignToUnit( PlayerUnit ) self:MessageToGroups( PlayerUnit:GetPlayerName() .. " joined Task " .. self:GetName() ) end end end return PlayerUnitAdded end --- Abort a PlayerUnit from a Task. -- If the Unit was not part of the Task, false is returned. -- If the Unit is part of the Task, true is returned. -- @param #TASK self -- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. -- @return #boolean true if Unit is part of the Task. function TASK:AbortUnit( PlayerUnit ) self:F( { PlayerUnit = PlayerUnit } ) local PlayerUnitAborted = false local PlayerGroups = self:GetGroups() local PlayerGroup = PlayerUnit:GetGroup() -- Is the PlayerGroup part of the PlayerGroups? if PlayerGroups:IsIncludeObject( PlayerGroup ) then -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. if self:IsStateAssigned() then local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) self:E( { IsAssignedToGroup = IsAssignedToGroup } ) if IsAssignedToGroup then local PlayerName = PlayerUnit:GetPlayerName() self:UnAssignFromUnit( PlayerUnit ) self:MessageToGroups( PlayerName .. " aborted Task " .. self:GetName() ) self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) if #PlayerGroup:GetUnits() == 1 then self:UnAssignFromGroup( PlayerGroup ) PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) self:RemoveMenuForGroup( PlayerGroup ) end self:Abort() self:PlayerAborted( PlayerUnit ) end end end return PlayerUnitAborted end --- A PlayerUnit crashed in a Task. Abort the Player. -- If the Unit was not part of the Task, false is returned. -- If the Unit is part of the Task, true is returned. -- @param #TASK self -- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. -- @return #boolean true if Unit is part of the Task. function TASK:CrashUnit( PlayerUnit ) self:F( { PlayerUnit = PlayerUnit } ) local PlayerUnitCrashed = false local PlayerGroups = self:GetGroups() local PlayerGroup = PlayerUnit:GetGroup() -- Is the PlayerGroup part of the PlayerGroups? if PlayerGroups:IsIncludeObject( PlayerGroup ) then -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. if self:IsStateAssigned() then local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) self:E( { IsAssignedToGroup = IsAssignedToGroup } ) if IsAssignedToGroup then self:UnAssignFromUnit( PlayerUnit ) self:MessageToGroups( PlayerUnit:GetPlayerName() .. " crashed in Task " .. self:GetName() ) self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) if #PlayerGroup:GetUnits() == 1 then PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) self:RemoveMenuForGroup( PlayerGroup ) end self:PlayerCrashed( PlayerUnit ) end end end return PlayerUnitCrashed end --- Gets the Mission to where the TASK belongs. -- @param #TASK self -- @return Tasking.Mission#MISSION function TASK:GetMission() return self.Mission end --- Gets the SET_GROUP assigned to the TASK. -- @param #TASK self -- @return Core.Set#SET_GROUP function TASK:GetGroups() return self.SetGroup end --- Assign the @{Task} to a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @return #TASK function TASK:AssignToGroup( TaskGroup ) self:F2( TaskGroup:GetName() ) local TaskGroupName = TaskGroup:GetName() TaskGroup:SetState( TaskGroup, "Assigned", self ) local Mission = self:GetMission() local MissionMenu = Mission:GetMenu( TaskGroup ) MissionMenu:RemoveSubMenus() --self:RemoveMenuForGroup( TaskGroup ) self:SetAssignedMenuForGroup( TaskGroup ) local TaskUnits = TaskGroup:GetUnits() for UnitID, UnitData in pairs( TaskUnits ) do local TaskUnit = UnitData -- Wrapper.Unit#UNIT local PlayerName = TaskUnit:GetPlayerName() self:E(PlayerName) if PlayerName ~= nil or PlayerName ~= "" then self:AssignToUnit( TaskUnit ) end end return self end --- -- @param #TASK self -- @param Wrapper.Group#GROUP FindGroup -- @return #boolean function TASK:HasGroup( FindGroup ) return self:GetGroups():IsIncludeObject( FindGroup ) end --- Assign the @{Task} to an alive @{Unit}. -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self function TASK:AssignToUnit( TaskUnit ) self:F( TaskUnit:GetName() ) local FsmTemplate = self:GetUnitProcess() -- Assign a new FsmUnit to TaskUnit. local FsmUnit = self:SetStateMachine( TaskUnit, FsmTemplate:Copy( TaskUnit, self ) ) -- Core.Fsm#FSM_PROCESS self:E({"Address FsmUnit", tostring( FsmUnit ) } ) FsmUnit:SetStartState( "Planned" ) FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. return self end --- UnAssign the @{Task} from an alive @{Unit}. -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self function TASK:UnAssignFromUnit( TaskUnit ) self:F( TaskUnit:GetName() ) self:RemoveStateMachine( TaskUnit ) return self end --- Sets the TimeOut for the @{Task}. If @{Task} stayed planned for longer than TimeOut, it gets into Cancelled status. -- @param #TASK self -- @param #integer Timer in seconds -- @return #TASK self function TASK:SetTimeOut ( Timer ) self:F( Timer ) self.TimeOut = Timer self:__TimeOut( self.TimeOut ) return self end --- Send a message of the @{Task} to the assigned @{Group}s. -- @param #TASK self function TASK:MessageToGroups( Message ) self:F( { Message = Message } ) local Mission = self:GetMission() local CC = Mission:GetCommandCenter() for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do local TaskGroup = TaskGroup -- Wrapper.Group#GROUP CC:MessageToGroup( Message, TaskGroup, TaskGroup:GetName() ) end end --- Send the briefng message of the @{Task} to the assigned @{Group}s. -- @param #TASK self function TASK:SendBriefingToAssignedGroups() self:F2() for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do if self:IsAssignedToGroup( TaskGroup ) then TaskGroup:Message( self.TaskBriefing, 60 ) end end end --- UnAssign the @{Task} from the @{Group}s. -- @param #TASK self function TASK:UnAssignFromGroups() self:F2() for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do self:UnAssignFromGroup( TaskGroup ) end end --- UnAssign the @{Task} from a @{Group}. -- @param #TASK self function TASK:UnAssignFromGroup( TaskGroup ) self:F2( { TaskGroup } ) TaskGroup:SetState( TaskGroup, "Assigned", nil ) self:RemoveAssignedMenuForGroup( TaskGroup ) local TaskUnits = TaskGroup:GetUnits() for UnitID, UnitData in pairs( TaskUnits ) do local TaskUnit = UnitData -- Wrapper.Unit#UNIT local PlayerName = TaskUnit:GetPlayerName() if PlayerName ~= nil or PlayerName ~= "" then self:UnAssignFromUnit( TaskUnit ) end end end --- Returns if the @{Task} is assigned to the Group. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @return #boolean function TASK:IsAssignedToGroup( TaskGroup ) local TaskGroupName = TaskGroup:GetName() if self:IsStateAssigned() then if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then self:T( { "Task is assigned to:", TaskGroup:GetName() } ) return true end end self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) return false end --- Returns if the @{Task} has still alive and assigned Units. -- @param #TASK self -- @return #boolean function TASK:HasAliveUnits() self:F() for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do if self:IsStateAssigned() then if self:IsAssignedToGroup( TaskGroup ) then for TaskUnitID, TaskUnit in pairs( TaskGroup:GetUnits() ) do if TaskUnit:IsAlive() then self:T( { HasAliveUnits = true } ) return true end end end end end self:T( { HasAliveUnits = false } ) return false end --- Set the menu options of the @{Task} to all the groups in the SetGroup. -- @param #TASK self -- @param #number MenuTime -- @return #TASK function TASK:SetMenu( MenuTime ) self:F() self.SetGroup:Flush() for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then -- Set Mission Menus local Mission = self:GetMission() local MissionMenu = Mission:GetMenu() if MissionMenu then TaskGroup.MenuReports = MENU_GROUP:New( TaskGroup, "Reports", MissionMenu ) MENU_GROUP_COMMAND:New( TaskGroup, "Report Tasks", TaskGroup.MenuReports, Mission.MenuReportSummary, Mission, TaskGroup ) MENU_GROUP_COMMAND:New( TaskGroup, "Report Planned Tasks", TaskGroup.MenuReports, Mission.MenuReportOverview, Mission, TaskGroup, "Planned" ) MENU_GROUP_COMMAND:New( TaskGroup, "Report Ongoing Tasks", TaskGroup.MenuReports, Mission.MenuReportOverview, Mission, TaskGroup, "Ongoing" ) MENU_GROUP_COMMAND:New( TaskGroup, "Report Successful Tasks", TaskGroup.MenuReports, Mission.MenuReportOverview, Mission, TaskGroup, "Success" ) MENU_GROUP_COMMAND:New( TaskGroup, "Report Failed Tasks", TaskGroup.MenuReports, Mission.MenuReportOverview, Mission, TaskGroup, "Failed" ) MENU_GROUP_COMMAND:New( TaskGroup, "Report Held Tasks", TaskGroup.MenuReports, Mission.MenuReportOverview, Mission, TaskGroup, "Hold" ) end if self:IsStatePlanned() or self:IsStateReplanned() then self:SetMenuForGroup( TaskGroup, MenuTime ) end end end end --- Set the Menu for a Group -- @param #TASK self -- @param #number MenuTime -- @return #TASK function TASK:SetMenuForGroup( TaskGroup, MenuTime ) if not TaskGroup:GetState( TaskGroup, "Assigned" ) then self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime ) else if not self:IsAssignedToGroup( TaskGroup ) then self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) end end end --- Set the planned menu option of the @{Task}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #string MenuText The menu text. -- @param #number MenuTime -- @return #TASK self function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() local MissionName = Mission:GetName() local CommandCenter = Mission:GetCommandCenter() local CommandCenterMenu = CommandCenter:GetMenu() local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) local MissionMenu = Mission:GetMenu( TaskGroup ) local TaskType = self:GetType() local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ):SetTime( MenuTime ) local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true ) return self end --- Set the assigned menu options of the @{Task}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #number MenuTime -- @return #TASK self function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() local MissionMenu = Mission:GetMenu( TaskGroup ) self:E( { MissionMenu = MissionMenu } ) local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ) local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ) return self end --- Remove the menu options of the @{Task} to all the groups in the SetGroup. -- @param #TASK self -- @param #number MenuTime -- @return #TASK function TASK:RemoveMenu( MenuTime ) self:F() for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do local TaskGroup = TaskGroup -- Wrapper.Group#GROUP if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then if not self:IsAssignedToGroup( TaskGroup ) then self:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) end end end end --- Remove the menu option of the @{Task} for a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #number MenuTime -- @return #TASK self function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) self:F() local Mission = self:GetMission() local MissionName = Mission:GetName() local MissionMenu = Mission:GetMenu( TaskGroup ) if MissionMenu then local TaskType = self:GetType() local TypeMenu = MissionMenu:GetMenu( TaskType ) if TypeMenu then local TaskMenu = TypeMenu:GetMenu( self:GetTaskName() ) if TaskMenu then TaskMenu:Remove( MenuTime ) end end end end --- Remove the assigned menu option of the @{Task} for a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #number MenuTime -- @return #TASK self function TASK:RemoveAssignedMenuForGroup( TaskGroup ) self:F() local Mission = self:GetMission() local MissionName = Mission:GetName() local MissionMenu = Mission:GetMenu( TaskGroup ) if MissionMenu then MissionMenu:RemoveSubMenus() end end function TASK.MenuAssignToGroup( MenuParam ) local self = MenuParam.self local TaskGroup = MenuParam.TaskGroup self:E( "Assigned menu selected") self:AssignToGroup( TaskGroup ) end --- Report the task status. -- @param #TASK self function TASK:MenuTaskStatus( TaskGroup ) local ReportText = self:ReportDetails() self:T( ReportText ) self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup ) end --- Report the task status. -- @param #TASK self function TASK:MenuTaskAbort( TaskGroup ) self:Abort() end --- Returns the @{Task} name. -- @param #TASK self -- @return #string TaskName function TASK:GetTaskName() return self.TaskName end --- Get the default or currently assigned @{Process} template with key ProcessName. -- @param #TASK self -- @param #string ProcessName -- @return Core.Fsm#FSM_PROCESS function TASK:GetProcessTemplate( ProcessName ) local ProcessTemplate = self.ProcessClasses[ProcessName] return ProcessTemplate end -- TODO: Obscolete? --- Fail processes from @{Task} with key @{Unit} -- @param #TASK self -- @param #string TaskUnitName -- @return #TASK self function TASK:FailProcesses( TaskUnitName ) for ProcessID, ProcessData in pairs( self.Processes[TaskUnitName] ) do local Process = ProcessData Process.Fsm:Fail() end end --- Add a FiniteStateMachine to @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Core.Fsm#FSM_PROCESS Fsm -- @return #TASK self function TASK:SetStateMachine( TaskUnit, Fsm ) self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil, Fsm:GetClassNameAndID() } ) self.Fsm[TaskUnit] = Fsm return Fsm end --- Gets the FiniteStateMachine of @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Fsm#FSM_PROCESS function TASK:GetStateMachine( TaskUnit ) self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) return self.Fsm[TaskUnit] end --- Remove FiniteStateMachines from @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self function TASK:RemoveStateMachine( TaskUnit ) self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) self:E( self.Fsm ) for TaskUnitT, Fsm in pairs( self.Fsm ) do self:E( TaskUnitT ) end self.Fsm[TaskUnit] = nil collectgarbage() self:E( "Garbage Collected, Processes should be finalized now ...") end --- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self function TASK:HasStateMachine( TaskUnit ) self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) return ( self.Fsm[TaskUnit] ~= nil ) end --- Gets the Scoring of the task -- @param #TASK self -- @return Functional.Scoring#SCORING Scoring function TASK:GetScoring() return self.Mission:GetScoring() end --- Gets the Task Index, which is a combination of the Task type, the Task name. -- @param #TASK self -- @return #string The Task ID function TASK:GetTaskIndex() local TaskType = self:GetType() local TaskName = self:GetName() return TaskType .. "." .. TaskName end --- Sets the Name of the Task -- @param #TASK self -- @param #string TaskName function TASK:SetName( TaskName ) self.TaskName = TaskName end --- Gets the Name of the Task -- @param #TASK self -- @return #string The Task Name function TASK:GetName() return self.TaskName end --- Sets the Type of the Task -- @param #TASK self -- @param #string TaskType function TASK:SetType( TaskType ) self.TaskType = TaskType end --- Sets the Information on the Task -- @param #TASK self -- @param #string TaskInfo function TASK:SetInfo( TaskInfo, TaskInfoText ) self.TaskInfo[TaskInfo] = TaskInfoText end --- Gets the Type of the Task -- @param #TASK self -- @return #string TaskType function TASK:GetType() return self.TaskType end --- Sets the ID of the Task -- @param #TASK self -- @param #string TaskID function TASK:SetID( TaskID ) self.TaskID = TaskID end --- Gets the ID of the Task -- @param #TASK self -- @return #string TaskID function TASK:GetID() return self.TaskID end --- Sets a @{Task} to status **Success**. -- @param #TASK self function TASK:StateSuccess() self:SetState( self, "State", "Success" ) return self end --- Is the @{Task} status **Success**. -- @param #TASK self function TASK:IsStateSuccess() return self:Is( "Success" ) end --- Sets a @{Task} to status **Failed**. -- @param #TASK self function TASK:StateFailed() self:SetState( self, "State", "Failed" ) return self end --- Is the @{Task} status **Failed**. -- @param #TASK self function TASK:IsStateFailed() return self:Is( "Failed" ) end --- Sets a @{Task} to status **Planned**. -- @param #TASK self function TASK:StatePlanned() self:SetState( self, "State", "Planned" ) return self end --- Is the @{Task} status **Planned**. -- @param #TASK self function TASK:IsStatePlanned() return self:Is( "Planned" ) end --- Sets a @{Task} to status **Aborted**. -- @param #TASK self function TASK:StateAborted() self:SetState( self, "State", "Aborted" ) return self end --- Is the @{Task} status **Aborted**. -- @param #TASK self function TASK:IsStateAborted() return self:Is( "Aborted" ) end --- Sets a @{Task} to status **Cancelled**. -- @param #TASK self function TASK:StateCancelled() self:SetState( self, "State", "Cancelled" ) return self end --- Is the @{Task} status **Cancelled**. -- @param #TASK self function TASK:IsStateCancelled() return self:Is( "Cancelled" ) end --- Sets a @{Task} to status **Assigned**. -- @param #TASK self function TASK:StateAssigned() self:SetState( self, "State", "Assigned" ) return self end --- Is the @{Task} status **Assigned**. -- @param #TASK self function TASK:IsStateAssigned() return self:Is( "Assigned" ) end --- Sets a @{Task} to status **Hold**. -- @param #TASK self function TASK:StateHold() self:SetState( self, "State", "Hold" ) return self end --- Is the @{Task} status **Hold**. -- @param #TASK self function TASK:IsStateHold() return self:Is( "Hold" ) end --- Sets a @{Task} to status **Replanned**. -- @param #TASK self function TASK:StateReplanned() self:SetState( self, "State", "Replanned" ) return self end --- Is the @{Task} status **Replanned**. -- @param #TASK self function TASK:IsStateReplanned() return self:Is( "Replanned" ) end --- Gets the @{Task} status. -- @param #TASK self function TASK:GetStateString() return self:GetState( self, "State" ) end --- Sets a @{Task} briefing. -- @param #TASK self -- @param #string TaskBriefing -- @return #TASK self function TASK:SetBriefing( TaskBriefing ) self.TaskBriefing = TaskBriefing return self end --- FSM function for a TASK -- @param #TASK self -- @param #string Event -- @param #string From -- @param #string To function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName ) self:E( { "Task Assigned", self.Dispatcher } ) self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned to your group." ) if self.Dispatcher then self:E( "Firing Assign event " ) self.Dispatcher:Assign( self, PlayerUnit, PlayerName ) end self:GetMission():__Start( 1 ) end --- FSM function for a TASK -- @param #TASK self -- @param #string Event -- @param #string From -- @param #string To function TASK:onenterSuccess( From, Event, To ) self:E( "Task Success" ) self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is successful! Good job!" ) self:UnAssignFromGroups() self:GetMission():__Complete( 1 ) end --- FSM function for a TASK -- @param #TASK self -- @param #string From -- @param #string Event -- @param #string To function TASK:onenterAborted( From, Event, To ) self:E( "Task Aborted" ) self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been aborted! Task may be replanned." ) self:UnAssignFromGroups() self:__Replan( 5 ) end --- FSM function for a TASK -- @param #TASK self -- @param #string From -- @param #string Event -- @param #string To function TASK:onafterReplan( From, Event, To ) self:E( "Task Replanned" ) self:GetMission():GetCommandCenter():MessageToCoalition( "Replanning Task " .. self:GetName() .. "." ) self:SetMenu() end --- FSM function for a TASK -- @param #TASK self -- @param #string From -- @param #string Event -- @param #string To function TASK:onenterFailed( From, Event, To ) self:E( "Task Failed" ) self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has failed!" ) self:UnAssignFromGroups() end --- FSM function for a TASK -- @param #TASK self -- @param #string Event -- @param #string From -- @param #string To function TASK:onstatechange( From, Event, To ) if self:IsTrace() then MESSAGE:New( "@ Task " .. self.TaskName .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() end if self.Scores[To] then local Scoring = self:GetScoring() if Scoring then self:E( { self.Scores[To].ScoreText, self.Scores[To].Score } ) Scoring:_AddMissionScore( self.Mission, self.Scores[To].ScoreText, self.Scores[To].Score ) end end end --- FSM function for a TASK -- @param #TASK self -- @param #string Event -- @param #string From -- @param #string To function TASK:onenterPlanned( From, Event, To) if not self.TimeOut == 0 then self.__TimeOut( self.TimeOut ) end end --- FSM function for a TASK -- @param #TASK self -- @param #string Event -- @param #string From -- @param #string To function TASK:onbeforeTimeOut( From, Event, To ) if From == "Planned" then self:RemoveMenu() return true end return false end do -- Dispatcher --- Set dispatcher of a task -- @param #TASK self -- @param Tasking.DetectionManager#DETECTION_MANAGER Dispatcher -- @return #TASK function TASK:SetDispatcher( Dispatcher ) self.Dispatcher = Dispatcher end end do -- Reporting --- Create a summary report of the Task. -- List the Task Name and Status -- @param #TASK self -- @return #string function TASK:ReportSummary() local Report = REPORT:New() -- List the name of the Task. local Name = self:GetName() -- Determine the status of the Task. local State = self:GetState() Report:Add( "Task " .. Name .. " - State '" .. State ) return Report:Text() end --- Create an overiew report of the Task. -- List the Task Name and Status -- @param #TASK self -- @return #string function TASK:ReportOverview() local Report = REPORT:New() -- List the name of the Task. local Name = self:GetName() -- Determine the status of the Task. local State = self:GetState() local Detection = self.TaskInfo["Detection"] and " - " .. self.TaskInfo["Detection"] or "" Report:Add( "Task " .. Name .. Detection ) return Report:Text() end --- Create a detailed report of the Task. -- List the Task Status, and the Players assigned to the Task. -- @param #TASK self -- @return #string function TASK:ReportDetails() local Report = REPORT:New() -- List the name of the Task. local Name = self:GetName() -- Determine the status of the Task. local State = self:GetState() -- Loop each Unit active in the Task, and find Player Names. local PlayerNames = {} local PlayerReport = REPORT:New( "\n Players:" ) for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP PlayerNames = PlayerGroup:GetPlayerNames() if PlayerNames then PlayerReport:Add( " Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) ) end end local Detection = self.TaskInfo["Detection"] and "\n Detection: " .. self.TaskInfo["Detection"] or "" local Changes = self.TaskInfo["Changes"] and "\n Changes: " .. self.TaskInfo["Changes"] or "" local Players = PlayerReport:Text() Report:Add( "Task " .. Name .. Players .. Detection .. Changes ) -- Loop each Process in the Task, and find Reporting Details. Report:Add( string.format( "Task %s\n -- State '%s'\n%s", Name, State, PlayerReport:Text() ) ) return Report:Text() end end -- Reporting