Jippie! Finally got the Sub Statemachine declaration understandable for end-users.

Reworked the hierarchical state machine processing in terms of sub
processing.
Now, the declaration and usage of subs is completely understandable and
easy to implement.
I am excited to see how end-users will see the possibilities.
This commit is contained in:
FlightControl 2016-11-22 08:45:38 +01:00
parent 07f6760039
commit 2f4eb39156
13 changed files with 296 additions and 179 deletions

View File

@ -116,9 +116,18 @@ function BASE:New()
self.__index = self
_ClassID = _ClassID + 1
self.ClassID = _ClassID
return self
end
function BASE:_Destructor()
--self:E("_Destructor")
self:EventRemoveAll()
end
--- This is the worker method to inherit from a parent class.
-- @param #BASE self
-- @param Child is the Child class that inherits.
@ -131,6 +140,22 @@ function BASE:Inherit( Child, Parent )
if Child ~= nil then
setmetatable( Child, Parent )
Child.__index = Child
local proxy = newproxy(true)
local proxyMeta = getmetatable(proxy)
proxyMeta.__gc = function ()
-- env.info("In __gc for " .. Child:GetClassNameAndID() )
if Child._Destructor then
Child:_Destructor()
end
end
-- keep the userdata from newproxy reachable until the object
-- table is about to be garbage-collected - then the __gc hook
-- will be invoked and the destructor called
rawset(Child, '__proxy', proxy)
end
--self:T( 'Inherited from ' .. Parent.ClassName )
return Child

View File

@ -85,9 +85,16 @@ end
-- @return #EVENT.Events
function EVENT:Init( EventID, EventClass )
self:F3( { _EVENTCODES[EventID], EventClass } )
if not self.Events[EventID] then
self.Events[EventID] = {}
-- Create a WEAK table to ensure that the garbage collector is cleaning the event links when the object usage is cleaned.
local Meta = {}
setmetatable( self.Events[EventID], Meta )
Meta.__mode = "k"
end
if not self.Events[EventID][EventClass] then
self.Events[EventID][EventClass] = {}
end

View File

@ -18,7 +18,7 @@
--- STATEMACHINE class
-- @type STATEMACHINE
-- @extends Base#BASE
-- @extends Core.Base#BASE
STATEMACHINE = {
ClassName = "STATEMACHINE",
}
@ -34,7 +34,7 @@ function STATEMACHINE:New( options )
--local self = routines.utils.deepCopy( self ) -- Create a new self instance
assert(options.events)
--assert(options.events)
--local MT = {}
--setmetatable( self, MT )
@ -46,31 +46,70 @@ function STATEMACHINE:New( options )
self.subs = {}
self.endstates = {}
for _, event in ipairs(options.events or {}) do
local name = event.name
local __name = "__" .. event.name
self[name] = self[name] or self:_create_transition(name)
self[__name] = self[__name] or self:_delayed_transition(name)
self:T( "Added methods: " .. name .. ", " .. __name )
self.events[name] = self.events[name] or { map = {} }
self:_add_to_map(self.events[name].map, event)
for _, event in pairs(options.events or {}) do
self:E({ "events", event })
self:_eventmap( self.events, event )
end
for name, callback in pairs(options.callbacks or {}) do
self:E("callbacks")
self[name] = callback
end
for name, sub in pairs( options.subs or {} ) do
self:E("sub")
self:_submap( self.subs, sub, name )
end
for name, endstate in pairs( options.endstates or {} ) do
self:E("endstate")
self.endstates[endstate] = endstate
end
return self
end
function STATEMACHINE:SetInitialState( State )
self.current = State
end
function STATEMACHINE:AddAction( From, Event, To )
local event = {}
event.from = From
event.name = Event
event.to = To
self:E( event )
self:_eventmap( self.events, event )
end
--- Set the default @{Process} template with key ProcessName providing the ProcessClass and the process object when it is assigned to a @{Controllable} by the task.
-- @return Process#PROCESS
function STATEMACHINE:AddProcess( From, Event, Process, ReturnEvents )
local sub = {}
sub.FromParent = From
sub.EventParent = Event
sub.fsm = Process
sub.event = "Start"
sub.ReturnEvents = ReturnEvents
self:_submap( self.subs, sub, nil )
self:AddAction( From, Event, "*" )
return Process
end
function STATEMACHINE:GetSubs()
return self.options.subs
end
function STATEMACHINE:LoadCallBacks( CallBackTable )
for name, callback in pairs( CallBackTable or {} ) do
@ -79,17 +118,29 @@ function STATEMACHINE:LoadCallBacks( CallBackTable )
end
function STATEMACHINE:_eventmap( events, event )
local name = event.name
local __name = "__" .. event.name
self[name] = self[name] or self:_create_transition(name)
self[__name] = self[__name] or self:_delayed_transition(name)
self:T( "Added methods: " .. name .. ", " .. __name )
events[name] = self.events[name] or { map = {} }
self:_add_to_map( events[name].map, event )
end
function STATEMACHINE:_submap( subs, sub, name )
self:E( { sub = sub, name = name } )
subs[sub.onstateparent] = subs[sub.onstateparent] or {}
subs[sub.onstateparent][sub.oneventparent] = subs[sub.onstateparent][sub.oneventparent] or {}
local Index = #subs[sub.onstateparent][sub.oneventparent] + 1
subs[sub.onstateparent][sub.oneventparent][Index] = {}
subs[sub.onstateparent][sub.oneventparent][Index].fsm = sub.fsm
subs[sub.onstateparent][sub.oneventparent][Index].event = sub.event
subs[sub.onstateparent][sub.oneventparent][Index].returnevents = sub.returnevents -- these events need to be given to find the correct continue event ... if none given, the processing will stop.
subs[sub.onstateparent][sub.oneventparent][Index].name = name
subs[sub.onstateparent][sub.oneventparent][Index].fsmparent = self
subs[sub.FromParent] = subs[sub.FromParent] or {}
subs[sub.FromParent][sub.EventParent] = subs[sub.FromParent][sub.EventParent] or {}
local Index = #subs[sub.FromParent][sub.EventParent] + 1
subs[sub.FromParent][sub.EventParent][Index] = {}
subs[sub.FromParent][sub.EventParent][Index].fsm = sub.fsm
subs[sub.FromParent][sub.EventParent][Index].event = sub.event
subs[sub.FromParent][sub.EventParent][Index].ReturnEvents = sub.ReturnEvents or {} -- these events need to be given to find the correct continue event ... if none given, the processing will stop.
subs[sub.FromParent][sub.EventParent][Index].name = name
subs[sub.FromParent][sub.EventParent][Index].fsmparent = self
end
@ -121,11 +172,15 @@ function STATEMACHINE._handler( self, EventName, ... )
local execute = true
local subtable = self:_gosub( to, EventName )
local subtable = self:_gosub( from, EventName )
for _, sub in pairs( subtable ) do
--if sub.nextevent then
-- self:F2( "nextevent = " .. sub.nextevent )
-- self[sub.nextevent]( self )
--end
self:F2( "calling sub: " .. sub.event )
sub.fsm.fsmparent = self
sub.fsm.returnevents = sub.returnevents
sub.fsm.ReturnEvents = sub.ReturnEvents
sub.fsm[sub.event]( sub.fsm )
execute = true
end
@ -176,32 +231,30 @@ function STATEMACHINE:_create_transition( EventName )
return function( self, ... ) return self._handler( self, EventName , ... ) end
end
function STATEMACHINE:_gosub( parentstate, parentevent )
function STATEMACHINE:_gosub( ParentFrom, ParentEvent )
local fsmtable = {}
if self.subs[parentstate] and self.subs[parentstate][parentevent] then
return self.subs[parentstate][parentevent]
self:E( { ParentFrom, ParentEvent, self.subs[ParentFrom] } )
if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then
return self.subs[ParentFrom][ParentEvent]
else
return {}
end
end
function STATEMACHINE:_isendstate( state )
local fsmparent = self.fsmparent
if fsmparent and self.endstates[state] then
self:E( { state = state, endstates = self.endstates, endstate = self.endstates[state] } )
local returnevent = nil
local fromstate = fsmparent.current
self:E( fromstate )
self:E( self.returnevents )
for _, eventname in pairs( self.returnevents ) do
local event = fsmparent.events[eventname]
self:E( event )
local to = event and event.map[fromstate] or event.map['*']
if to and to == state then
return fsmparent, eventname
function STATEMACHINE:_isendstate( Current )
local FSMParent = self.fsmparent
if FSMParent and self.endstates[Current] then
self:E( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } )
FSMParent.current = Current
local ParentFrom = FSMParent.current
self:E( ParentFrom )
self:E( self.ReturnEvents )
local Event = self.ReturnEvents[Current]
self:E( { ParentFrom, Event, self.ReturnEvents } )
if Event then
return FSMParent, Event
else
self:E( { "could not find parent event name for state", fromstate, to } )
end
self:E( { "Could not find parent event name for state ", ParentFrom } )
end
end
@ -209,6 +262,7 @@ function STATEMACHINE:_isendstate( state )
end
function STATEMACHINE:_add_to_map(map, event)
self:E( { map, event } )
if type(event.from) == 'string' then
map[event.from] = event.to
else
@ -216,6 +270,7 @@ function STATEMACHINE:_add_to_map(map, event)
map[from] = event.to
end
end
self:E( { map, event } )
end
function STATEMACHINE:is(state)
@ -224,6 +279,7 @@ end
function STATEMACHINE:can(e)
local event = self.events[e]
self:E( { self.current, event } )
local to = event and event.map[self.current] or event.map['*']
return to ~= nil, to
end
@ -290,7 +346,7 @@ end
--- STATEMACHINE_CONTROLLABLE class
-- @type STATEMACHINE_CONTROLLABLE
-- @field Controllable#CONTROLLABLE Controllable
-- @extends StateMachine#STATEMACHINE
-- @extends Core.StateMachine#STATEMACHINE
STATEMACHINE_CONTROLLABLE = {
ClassName = "STATEMACHINE_CONTROLLABLE",
}

View File

@ -6,7 +6,7 @@
-- @field Group#GROUP ProcessGroup
-- @field Menu#MENU_GROUP MissionMenu
-- @field #string ProcessName
-- @extends StateMachine#STATEMACHINE_CONTROLLABLE
-- @extends Core.StateMachine#STATEMACHINE_CONTROLLABLE
PROCESS = {
ClassName = "PROCESS",
NextEvent = nil,
@ -29,6 +29,7 @@ function PROCESS:New( FSMT, ProcessName, ProcessUnit )
--self.MissionMenu = Task.Mission:GetMissionMenu( self.ProcessGroup )
self.ProcessName = ProcessName
return self
end
@ -67,15 +68,6 @@ function PROCESS:GetMission()
return self.ProcessTask.Mission
end
function PROCESS:ProcessStart()
end
function PROCESS:ProcessStop()
self:E("ProcessStop Base Class")
self:EventRemoveAll()
end
--- Assign the process to a @{Unit} and activate the process.
-- @param #PROCESS self
@ -86,8 +78,6 @@ function PROCESS:Assign( ProcessTask, ProcessUnit )
self:SetControllable( ProcessUnit )
self:SetTask( ProcessTask )
self:ProcessStart()
self.ProcessGroup = ProcessUnit:GetGroup()
--self:Activate()

View File

@ -124,9 +124,12 @@ do -- PROCESS_ACCOUNT
-- @param #string To
function PROCESS_ACCOUNT:onafterStart( ProcessUnit, Event, From, To )
self:EventOnDead( self.EventDead )
self:__Wait( 1 )
end
--- StateMachine callback function
-- @param #PROCESS_ACCOUNT self
-- @param Controllable#CONTROLLABLE ProcessUnit
@ -170,6 +173,15 @@ do -- PROCESS_ACCOUNT_DEADS
}
--- Creates a new DESTROY process.
-- @param #PROCESS_ACCOUNT_DEADS self
-- @param Set#SET_UNIT TargetSetUnit
-- @param #string TaskName
function PROCESS_ACCOUNT_DEADS:Template( TargetSetUnit, TaskName )
return { self, arg }
end
--- Creates a new DESTROY process.
-- @param #PROCESS_ACCOUNT_DEADS self
-- @param Set#SET_UNIT TargetSetUnit
@ -186,8 +198,11 @@ do -- PROCESS_ACCOUNT_DEADS
return self
end
function PROCESS_ACCOUNT_DEADS:ProcessStart()
self:EventOnDead( self.EventDead )
function PROCESS_ACCOUNT_DEADS:_Destructor()
self:E("_Destructor")
self:RemoveEventsAll()
end
--- Process Events

View File

@ -134,6 +134,14 @@ do -- PROCESS_ASSIGN_ACCEPT
}
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
-- @param #PROCESS_ASSIGN_ACCEPT self
-- @param #string TaskBriefing
function PROCESS_ASSIGN_ACCEPT:Template( TaskBriefing )
return { self, { TaskBriefing } }
end
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
-- @param #PROCESS_ASSIGN_ACCEPT self
-- @param #string TaskBriefing

View File

@ -107,7 +107,7 @@ do -- PROCESS_ROUTE
{ name = 'Fail', from = '*', to = 'Failed' },
},
endstates = {
'Arrived', 'Failed', 'Success'
'Arrived', 'Failed'
},
}
@ -150,10 +150,10 @@ do -- PROCESS_ROUTE
-- @param #string Event
-- @param #string From
-- @param #string To
function PROCESS_ROUTE:onafterRoute( ProcessUnit, Event, From, To )
function PROCESS_ROUTE:onbeforeRoute( ProcessUnit, Event, From, To )
if ProcessUnit:IsAlive() then
local HasArrived = self:HasArrived( ProcessUnit )
local HasArrived = self:HasArrived( ProcessUnit ) -- Polymorphic
if self.DisplayCount >= self.DisplayInterval then
self:T( { HasArrived = HasArrived } )
if not HasArrived then
@ -165,7 +165,12 @@ do -- PROCESS_ROUTE
end
self:T( { DisplayCount = self.DisplayCount } )
if HasArrived then
self:__Arrive( 1 )
else
self:__Route( 1 )
end
return HasArrived -- if false, then the event will not be executed...
end
@ -191,6 +196,14 @@ do -- PROCESS_ROUTE_ZONE
}
--- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE.
-- @param #PROCESS_ROUTE_ZONE self
-- @param Zone#ZONE_BASE TargetZone
function PROCESS_ROUTE_ZONE:Template( TargetZone )
return { self, arg }
end
--- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE.
-- @param #PROCESS_ROUTE_ZONE self
-- @param Zone#ZONE_BASE TargetZone
@ -206,9 +219,15 @@ do -- PROCESS_ROUTE_ZONE
--- Method override to check if the controllable has arrived.
-- @param #PROCESS_ROUTE self
-- @param Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @return #boolean
function PROCESS_ROUTE_ZONE:HasArrived( ProcessUnit )
if ProcessUnit:IsInZone( self.TargetZone ) then
local RouteText = ProcessUnit:GetCallsign() .. ": You have arrived within the zone!"
MESSAGE:New( RouteText, self.DisplayTime, self.DisplayCategory ):ToGroup( ProcessUnit:GetGroup() )
end
return ProcessUnit:IsInZone( self.TargetZone )
end

View File

@ -144,13 +144,20 @@ do -- PROCESS_SMOKE_TARGETS_ZONE
ClassName = "PROCESS_SMOKE_TARGETS_ZONE",
}
function PROCESS_SMOKE_TARGETS_ZONE:ProcessStop()
self:E("ProcessStop Detailed")
function PROCESS_SMOKE_TARGETS_ZONE:_Destructor()
self:E("_Destructor")
self.Menu:Remove()
self:EventRemoveAll()
end
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
-- @param #PROCESS_SMOKE_TARGETS_ZONE self
-- @param Set#SET_UNIT TargetSetUnit
-- @param Zone#ZONE_BASE TargetZone
function PROCESS_SMOKE_TARGETS_ZONE:Template( TargetSetUnit, TargetZone )
return { self, arg }
end
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
-- @param #PROCESS_SMOKE_TARGETS_ZONE self

View File

@ -9,8 +9,8 @@
-- * @{#TASK_BASE.AssignToGroup}():Assign a task to a group (of players).
-- * @{#TASK_BASE.AddProcess}():Add a @{Process} to a task.
-- * @{#TASK_BASE.RemoveProcesses}():Remove a running @{Process} from a running task.
-- * @{#TASK_BASE.AddStateMachine}():Add a @{StateMachine} to a task.
-- * @{#TASK_BASE.RemoveStateMachines}():Remove @{StateMachine}s from a task.
-- * @{#TASK_BASE.SetStateMachine}():Set a @{StateMachine} to a task.
-- * @{#TASK_BASE.RemoveStateMachine}():Remove @{StateMachine} from a task.
-- * @{#TASK_BASE.HasStateMachine}():Enquire if the task has a @{StateMachine}
-- * @{#TASK_BASE.AssignToUnit}(): Assign a task to a unit. (Needs to be implemented in the derived classes from @{#TASK_BASE}.
-- * @{#TASK_BASE.UnAssignFromUnit}(): Unassign the task from a unit.
@ -69,13 +69,13 @@ TASK_BASE = {
--- Instantiates a new TASK_BASE. Should never be used. Interface Class.
-- @param #TASK_BASE self
-- @param Mission#MISSION The mission wherein the Task is registered.
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param Mission#MISSION Mission The mission wherein the Task is registered.
-- @param 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
-- @param #string TaskCategory The category of the Task (A2G, A2A, Transport, ... )
-- @return #TASK_BASE self
function TASK_BASE:New( Mission, SetGroup, TaskName, TaskType, TaskCategory )
function TASK_BASE:New( Mission, SetGroupAssign, TaskName, TaskType, TaskCategory )
local self = BASE:Inherit( self, BASE:New() )
self:E( "New TASK " .. TaskName )
@ -84,7 +84,7 @@ function TASK_BASE:New( Mission, SetGroup, TaskName, TaskType, TaskCategory )
self.Fsm = {}
self.Mission = Mission
self.SetGroup = SetGroup
self.SetGroup = SetGroupAssign
self:SetCategory( TaskCategory )
self:SetType( TaskType )
@ -93,6 +93,8 @@ function TASK_BASE:New( Mission, SetGroup, TaskName, TaskType, TaskCategory )
self.TaskBriefing = "You are assigned to the task: " .. self.TaskName .. "."
self.FsmTemplate = self.FsmTemplate or STATEMACHINE_TASK:New( {}, self )
return self
end
@ -109,6 +111,10 @@ function TASK_BASE:CleanUp()
return nil
end
function TASK_BASE:GetFsmTemplate()
return self.FsmTemplate
end
--- Assign the @{Task}to a @{Group}.
-- @param #TASK_BASE self
@ -128,6 +134,7 @@ function TASK_BASE:AssignToGroup( TaskGroup )
for UnitID, UnitData in pairs( TaskUnits ) do
local TaskUnit = UnitData -- Unit#UNIT
local PlayerName = TaskUnit:GetPlayerName()
self:E(PlayerName)
if PlayerName ~= nil or PlayerName ~= "" then
self:AssignToUnit( TaskUnit )
end
@ -136,6 +143,65 @@ function TASK_BASE:AssignToGroup( TaskGroup )
return self
end
--- Assign the @{Task} to an alive @{Unit}.
-- @param #TASK_BASE self
-- @param Unit#UNIT TaskUnit
-- @return #TASK_BASE self
function TASK_BASE:AssignToUnit( TaskUnit )
self:F( TaskUnit:GetName() )
-- Copy the FsmTemplate, which is not assigned to a Unit.
-- Assign the FsmTemplate to the TaskUnit.
local FsmTemplate = self:GetFsmTemplate()
local FsmUnit = UTILS.DeepCopy( FsmTemplate )
FsmUnit:Assign( self, TaskUnit )
-- Assign each FsmSub in FsmUnit to the TaskUnit.
-- (This is not done during the copy).
for FsmSubID, FsmSub in ipairs( FsmUnit:GetSubs() ) do
self:E( { "Sub ID", FsmSub:GetClassNameAndID() } )
FsmSub:Assign( self, TaskUnit )
end
-- for TransitionID, TransitionTemplate in ipairs( self.TransitionTemplates ) do
-- self:E( TransitionTemplate )
-- FSM:AddTransition( TransitionTemplate.From, TransitionTemplate.Event, TransitionTemplate.To )
-- end
-- Copy each ProcessTemplate for the TaskUnit that is alive, as set as a template at the Parent.
-- Each Process will start From a state, upon a fired Event.
-- Upon finalization of the Process, the ReturnEvents contain for which Return state which Event of the Parent needs to be fired.
-- The Return state of the Process is transferred to the Parent.
-- for ProcessID, ProcessTemplate in ipairs( self.ProcessTemplates ) do
-- FSM:AddProcess( ProcessTemplate.From, ProcessTemplate.Event, Process, ProcessTemplate.ReturnEvents )
-- self:E( { "Process ID", Process:GetClassNameAndID() } )
-- Process:Assign( self, TaskUnit )
-- end
FsmUnit:SetInitialState( "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_BASE self
-- @param Unit#UNIT TaskUnit
-- @return #TASK_BASE self
function TASK_BASE:UnAssignFromUnit( TaskUnitName )
self:F( TaskUnitName )
if self:HasStateMachine( TaskUnitName ) == true then
self:E("RemoveStateMachines")
self:RemoveStateMachine( TaskUnitName )
end
return self
end
--- Send the briefng message of the @{Task} to the assigned @{Group}s.
-- @param #TASK_BASE self
function TASK_BASE:SendBriefingToAssignedGroups()
@ -186,33 +252,6 @@ function TASK_BASE:IsAssignedToGroup( TaskGroup )
return false
end
--- Assign the @{Task} to an alive @{Unit}.
-- @param #TASK_BASE self
-- @param Unit#UNIT TaskUnit
-- @return #TASK_BASE self
function TASK_BASE:AssignToUnit( TaskUnit )
self:F( TaskUnit:GetName() )
return nil
end
--- UnAssign the @{Task} from an alive @{Unit}.
-- @param #TASK_BASE self
-- @param Unit#UNIT TaskUnit
-- @return #TASK_BASE self
function TASK_BASE:UnAssignFromUnit( TaskUnitName )
self:F( TaskUnitName )
if self:HasStateMachine( TaskUnitName ) == true then
self:E("RemoveStateMachines")
self:RemoveStateMachines( TaskUnitName )
self:E("RemoveProcesses")
self:RemoveProcesses( TaskUnitName )
end
return self
end
--- Set the menu options of the @{Task} to all the groups in the SetGroup.
-- @param #TASK_BASE self
-- @return #TASK_BASE self
@ -406,31 +445,6 @@ function TASK_BASE:GetTaskName()
end
--- This is the key worker function for the class. Instantiate a new Process based on the ProcessName to @{Task} and assign it to the ProcessUnit.
-- @param #TASK_BASE self
-- @param Unit#UNIT ProcessUnit The unit to which the process should be assigned.
-- @param #string ProcessName The name of the Process.
-- @return Process#PROCESS The Process that was added.
function TASK_BASE:AssignProcess( ProcessUnit, ProcessName )
self:F( { ProcessName } )
local ProcessUnitName = ProcessUnit:GetName()
-- Create the Process instance base on the ProcessClasses collection assigned to the Task
local ProcessTemplate, ProcessArguments
ProcessTemplate = self:GetProcessTemplate( ProcessName )
self:E( "Deepcopy" )
-- This statement copies the process template assigned to the task and creates a new process.
local Process = UTILS.DeepCopy( ProcessTemplate ) -- Fsm.Process#PROCESS
Process:Assign( self, ProcessUnit )
self.Processes = self.Processes or {}
self.Processes[ProcessUnitName] = self.Processes[ProcessUnitName] or {}
self.Processes[ProcessUnitName][ProcessName] = Process
return Process
end
--- Get the default or currently assigned @{Process} template with key ProcessName.
@ -445,35 +459,6 @@ function TASK_BASE:GetProcessTemplate( ProcessName )
end
--- Set the default @{Process} template with key ProcessName providing the ProcessClass and the process object when it is assigned to a @{Controllable} by the task.
-- @param #TASK_BASE self
-- @param #string ProcessName
-- @param Process#PROCESS ProcessTemplate
-- @return Process#PROCESS
function TASK_BASE:SetProcessTemplate( ProcessName, ProcessTemplate )
self.ProcessClasses[ProcessName] = ProcessTemplate
return ProcessTemplate
end
--- Remove Processes from @{Task} with key @{Unit}
-- @param #TASK_BASE self
-- @param #string TaskUnitName
-- @return #TASK_BASE self
function TASK_BASE:RemoveProcesses( TaskUnitName )
self:E( TaskUnitName )
for ProcessID, ProcessData in pairs( self.Processes[TaskUnitName] ) do
local Process = ProcessData -- Process.Process#PROCESS
Process:ProcessStop()
Process = nil
self.Processes[TaskUnitName][ProcessID] = nil
self:E( self.Processes[TaskUnitName][ProcessID] )
end
self.Processes[TaskUnitName] = nil
end
--- Fail processes from @{Task} with key @{Unit}
-- @param #TASK_BASE self
@ -491,10 +476,10 @@ end
-- @param #TASK_BASE self
-- @param Unit#UNIT TaskUnit
-- @return #TASK_BASE self
function TASK_BASE:AddStateMachine( TaskUnit, Fsm )
function TASK_BASE:SetStateMachine( TaskUnit, Fsm )
local TaskUnitName = TaskUnit:GetName()
self.Fsm[TaskUnitName] = self.Fsm[TaskUnitName] or {}
self.Fsm[TaskUnitName][#self.Fsm[TaskUnitName]+1] = Fsm
self.Fsm[TaskUnitName] = Fsm
return Fsm
end
@ -502,14 +487,10 @@ end
-- @param #TASK_BASE self
-- @param #string TaskUnitName
-- @return #TASK_BASE self
function TASK_BASE:RemoveStateMachines( TaskUnitName )
function TASK_BASE:RemoveStateMachine( TaskUnitName )
for _, Fsm in pairs( self.Fsm[TaskUnitName] ) do
Fsm = nil
self.Fsm[TaskUnitName][_] = nil
self:E( self.Fsm[TaskUnitName][_] )
end
self.Fsm[TaskUnitName] = nil
collectgarbage()
end
--- Checks if there is a FiniteStateMachine assigned to @{Unit} for @{Task}
@ -875,7 +856,7 @@ function TASK_BASE:OnStateChange( TaskUnit, Fsm, Event, From, To )
MESSAGE:New( "Task " .. self.TaskName .. " : " .. Event .. " changed to state " .. To, 15 ):ToAll()
end
self:E( { Event, From, To } )
self:T2( { Event, From, To } )
self:SetState( self, "State", To )
if self.Scores[To] then

View File

@ -69,7 +69,6 @@ do -- TASK_SEAD
end
--- Assign the @{Task} to a @{Unit}.
-- @param #TASK_SEAD self
-- @param Unit#UNIT TaskUnit
@ -85,7 +84,7 @@ do -- TASK_SEAD
local FSMT = {
initial = 'None',
events = {
{ name = 'Next', from = 'None', to = 'Planned' },
{ name = 'Start', from = 'None', to = 'Planned' },
{ name = 'Next', from = 'Planned', to = 'Assigned' },
{ name = 'Reject', from = 'Planned', to = 'Rejected' },
{ name = 'Next', from = 'Assigned', to = 'Success' },

View File

@ -1,4 +1,6 @@
env.info( "Lua Version = " .. _VERSION )
local Mission = MISSION:New( 'SEAD Targets', "Strategic", "SEAD the enemy", coalition.side.RED )
local Scoring = SCORING:New( "SEAD" )
@ -9,23 +11,31 @@ local TargetSet = SET_UNIT:New():FilterPrefixes( "US Hawk SR" ):FilterOnce()
local TargetZone = ZONE:New( "Target Zone" )
local TaskSEAD = TASK_SEAD
:New( Mission, SEADSet, "SEAD Radars", TargetSet, TargetZone )
local TaskSEAD = TASK_BASE:New( Mission, SEADSet, "SEAD Radars", "A2G", "SEAD" ) -- Tasking.Task#TASK_BASE
--:New( Mission, SEADSet, "SEAD Radars", TargetSet, TargetZone )
TaskSEAD:AddScore( "Success", "Destroyed all target radars", 250 )
TaskSEAD:AddScore( "Failed", "Failed to destroy all target radars", -100 )
local FsmSEAD = TaskSEAD:GetFsmTemplate()
local AssignProcess = TaskSEAD:SetProcessTemplate( "ASSIGN", PROCESS_ASSIGN_MENU_ACCEPT:New( "SEAD", "Hello World" ) )
AssignProcess:AddScore( TaskSEAD, "Assign", "You are assigned to the task", 10 )
FsmSEAD:AddProcess( "Planned", "Accept", PROCESS_ASSIGN_ACCEPT:New( "SEAD the Area" ), { Assigned = "Route", Rejected = "Eject" } )
FsmSEAD:AddProcess( "Assigned", "Route", PROCESS_ROUTE_ZONE:New( TargetZone, 3000 ), { Arrived = "Update" } )
FsmSEAD:AddAction ( "Rejected", "Eject", "Planned" )
FsmSEAD:AddAction ( "Arrived", "Update", "Updated" )
FsmSEAD:AddProcess( "Updated", "Account", PROCESS_ACCOUNT_DEADS:New( TargetSet, "SEAD" ), { Destroyed = "Success" } )
FsmSEAD:AddProcess( "Updated", "Smoke", PROCESS_SMOKE_TARGETS_ZONE:New( TargetSet, TargetZone ) )
FsmSEAD:AddAction ( "Destroyed", "Success", "Success" )
FsmSEAD:AddAction ( "Failed", "Fail", "Failed" )
local AccountProcess = TaskSEAD:SetProcessTemplate( "ACCOUNT", PROCESS_ACCOUNT_DEADS:New( TargetSet, "SEAD" ) )
AccountProcess:AddScore( TaskSEAD, "Account", "destroyed a radar", 25 )
AccountProcess:AddScore( TaskSEAD, "Failed", "failed to destroy a radar", -100 )
--TaskSEAD:AddScoreTask( "Success", "Destroyed all target radars", 250 )
--TaskSEAD:AddScoreTask( "Failed", "Failed to destroy all target radars", -100 )
--TaskSEAD:AddScoreProcess( "Account", "Account", "destroyed a radar", 25 )
--TaskSEAD:AddScoreProcess( "Smoke", "Failed", "failed to destroy a radar", -100 )
--local SmokeProcess = TaskSEAD:SetProcessTemplate( "SMOKE", PROCESS_SMOKE_TARGETS_ZONE:New( TargetSet, TargetZone ) )
--SmokeProcess:SetAttackGroup( GROUP:FindByName( "SmokeGroup" ), "Watchdog" )
--SmokeProcess:AddScore( TaskSEAD, "Account", "destroyed a radar", 25 )
--SmokeProcess:AddScore( TaskSEAD, "Failed", "failed to destroy a radar", -100 )
function FsmSEAD:onenterUpdated( TaskUnit )
TaskSEAD:Account()
TaskSEAD:Smoke()
end
-- Needs to be checked, should not be necessary ...
TaskSEAD:AssignToGroup( SEADSet:Get( "Test SEAD" ) )
Mission:AddTask( TaskSEAD )