diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 194f44154..ebbfb67dd 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -132,7 +132,7 @@ function BASE:_SetDestructor() -- TODO: Okay, this is really technical... -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak... -- Therefore, I am parking this logic until I've properly discussed all this with the community. - -- + --[[ local proxy = newproxy(true) local proxyMeta = getmetatable(proxy) @@ -147,7 +147,7 @@ function BASE:_SetDestructor() -- table is about to be garbage-collected - then the __gc hook -- will be invoked and the destructor called rawset( self, '__proxy', proxy ) - -- + --]] end --- This is the worker method to inherit from a parent class. diff --git a/Moose Development/Moose/Core/Timer.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua similarity index 55% rename from Moose Development/Moose/Core/Timer.lua rename to Moose Development/Moose/Core/ScheduleDispatcher.lua index 662506fc6..8193086bb 100644 --- a/Moose Development/Moose/Core/Timer.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -1,10 +1,29 @@ ---- This module contains the TIMER class. +--- This module defines the SCHEDULEDISPATCHER class, which is used by a central object called _SCHEDULEDISPATCHER. -- -- === -- --- Takes care of scheduled function dispatching for defined in MOOSE classes. +-- Takes care of the creation and dispatching of scheduled functions for SCHEDULER objects. -- --- This function is complicated. +-- This class is tricky and needs some thorought explanation. +-- SCHEDULE classes are used to schedule functions for objects, or as persistent objects. +-- The SCHEDULEDISPATCHER class ensures that: +-- +-- - Scheduled functions are planned according the SCHEDULER object parameters. +-- - Scheduled functions are repeated when requested, according the SCHEDULER object parameters. +-- - Scheduled functions are automatically removed when the schedule is finished, according the SCHEDULER object parameters. +-- +-- The SCHEDULEDISPATCHER class will manage SCHEDULER object in memory during garbage collection: +-- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER +-- object is _persistent_ within memory. +-- - When a SCHEDULER object *is* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection! +-- The none persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collectged, when the parent object is also desroyed or nillified and garbage collected. +-- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object, +-- these will not be executed anymore when the SCHEDULER object has been destroyed. +-- +-- The SCHEDULEDISPATCHER allows multiple scheduled functions to be planned and executed for one SCHEDULER object. +-- The SCHEDULER object therefore keeps a table of "CallID's", which are returned after each planning of a new scheduled function by the SCHEDULEDISPATCHER. +-- The SCHEDULER object plans new scheduled functions through the @{Core.Scheduler#SCHEDULER.Schedule}() method. +-- The Schedule() method returns the CallID that is the reference ID for each planned schedule. -- -- === -- @@ -13,16 +32,16 @@ -- ### Contributions: - -- ### Authors: FlightControl : Design & Programming -- --- @module Timer +-- @module ScheduleDispatcher ---- The TIMER structure --- @type TIMER -TIMER = { - ClassName = "TIMER", +--- The SCHEDULEDISPATCHER structure +-- @type SCHEDULEDISPATCHER +SCHEDULEDISPATCHER = { + ClassName = "SCHEDULEDISPATCHER", CallID = 0, } -function TIMER:New() +function SCHEDULEDISPATCHER:New() local self = BASE:Inherit( self, BASE:New() ) self:F3() return self @@ -32,9 +51,9 @@ end -- The development of this method was really tidy. -- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is nillified. -- Nothing of this code should be modified without testing it thoroughly. --- @param #TIMER self +-- @param #SCHEDULEDISPATCHER self -- @param Core.Scheduler#SCHEDULER Scheduler -function TIMER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop ) +function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop ) self:F( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop } ) self.CallID = self.CallID + 1 @@ -128,15 +147,13 @@ function TIMER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Star ( Randomize * Repeat / 2 ) ) + 0.01 - self:T3( { ScheduleArguments, "Repeat:", CurrentTime, ScheduleTime } ) + self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } ) return ScheduleTime -- returns the next time the function needs to be called. else - timer.removeFunction( ScheduleID ) - ScheduleID = nil + self:Stop( Scheduler, CallID ) end else - timer.removeFunction( ScheduleID ) - ScheduleID = nil + self:Stop( Scheduler, CallID ) end else --self:E( "Scheduled obscolete call for CallID: " .. CallID ) @@ -145,29 +162,49 @@ function TIMER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Star return nil end - - self.Schedule[Scheduler][self.CallID].ScheduleID = timer.scheduleFunction( - self.Schedule[Scheduler][self.CallID].CallHandler, - self.CallID, - timer.getTime() + self.Schedule[Scheduler][self.CallID].Start - ) + self:Start( Scheduler, self.CallID ) return self.CallID end -function TIMER:RemoveSchedule( CallID ) - self:F( CallID ) +function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID ) + self:F( { Remove = CallID, Scheduler = Scheduler } ) - local Schedule = self.ObjectSchedulers[CallID] - - if Schedule then - local ScheduleID = Schedule.ScheduleID - timer.removeFunction( ScheduleID ) - ScheduleID = nil - Schedule = nil + if CallID then + self:Stop( Scheduler, CallID ) + self.Schedule[Scheduler][CallID] = nil + end +end + +function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) + self:F( { Start = CallID, Scheduler = Scheduler } ) + + if CallID then + local Schedule = self.Schedule[Scheduler] + Schedule[CallID].ScheduleID = timer.scheduleFunction( + Schedule[CallID].CallHandler, + CallID, + timer.getTime() + Schedule[CallID].Start + ) + else + for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do + self:Start( Scheduler, CallID ) -- Recursive + end + end +end + +function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) + self:F( { Stop = CallID, Scheduler = Scheduler } ) + + if CallID then + local Schedule = self.Schedule[Scheduler] + timer.removeFunction( Schedule[CallID].ScheduleID ) + else + for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do + self:Stop( Scheduler, CallID ) -- Recursive + end end end - diff --git a/Moose Development/Moose/Core/Scheduler.lua b/Moose Development/Moose/Core/Scheduler.lua index ce54f70d8..2b953e3cb 100644 --- a/Moose Development/Moose/Core/Scheduler.lua +++ b/Moose Development/Moose/Core/Scheduler.lua @@ -1,36 +1,43 @@ --- This module contains the SCHEDULER class. -- --- 1) @{Core.Scheduler#SCHEDULER} class, extends @{Core.Base#BASE} --- ===================================================== --- The @{Core.Scheduler#SCHEDULER} class models time events calling given event handling functions. +-- # 1) @{Core.Scheduler#SCHEDULER} class, extends @{Core.Base#BASE} +-- +-- The @{Core.Scheduler#SCHEDULER} class creates schedule. -- --- 1.1) SCHEDULER constructor --- -------------------------- --- The SCHEDULER class is quite easy to use: +-- ## 1.1) SCHEDULER constructor +-- +-- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters: -- --- * @{Core.Scheduler#SCHEDULER.New}: Setup a new scheduler and start it with the specified parameters. +-- * @{Core.Scheduler#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection. +-- * @{Core.Scheduler#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection. +-- * @{Core.Scheduler#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. +-- * @{Core.Scheduler#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. +-- +-- ## 1.2) SCHEDULER timer stopping and (re-)starting. -- --- 1.2) SCHEDULER timer stop and start --- ----------------------------------- -- The SCHEDULER can be stopped and restarted with the following methods: -- --- * @{Core.Scheduler#SCHEDULER.Start}: (Re-)Start the scheduler. --- * @{Core.Scheduler#SCHEDULER.Stop}: Stop the scheduler. +-- * @{Core.Scheduler#SCHEDULER.Start}(): (Re-)Start the schedules within the SCHEDULER object. If a CallID is provided to :Start(), only the schedule referenced by CallID will be (re-)started. +-- * @{Core.Scheduler#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped. -- --- 1.3) Reschedule new time event --- ------------------------------ --- With @{Core.Scheduler#SCHEDULER.Schedule} a new time event can be scheduled. +-- ## 1.3) Create a new schedule +-- +-- With @{Core.Scheduler#SCHEDULER.Schedule}() a new time event can be scheduled. This function is used by the :New() constructor when a new schedule is planned. -- -- === -- -- ### Contributions: -- --- * Mechanist : Concept & Testing +-- * FlightControl : Concept & Testing -- -- ### Authors: -- -- * FlightControl : Design & Programming -- +-- ### Test Missions: +-- +-- * SCH - Scheduler +-- -- === -- -- @module Scheduler @@ -55,19 +62,24 @@ SCHEDULER = { -- @param #number RandomizationFactor Specifies a randomization factor between 0 and 1 to randomize the RepeatSecondsInterval. -- @param #number StopSeconds Specifies the amount of seconds when the scheduler will be stopped. -- @return #SCHEDULER self +-- @return #number The ScheduleID of the planned schedule. function SCHEDULER:New( TimeEventObject, TimeEventFunction, TimeEventFunctionArguments, StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds ) local self = BASE:Inherit( self, BASE:New() ) self:F2( { StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds } ) - self:Schedule( TimeEventObject, TimeEventFunction, TimeEventFunctionArguments, StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds ) + local ScheduleID = nil + + if TimeEventFunction then + ScheduleID = self:Schedule( TimeEventObject, TimeEventFunction, TimeEventFunctionArguments, StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds ) + end - return self + return self, ScheduleID end --function SCHEDULER:_Destructor() -- --self:E("_Destructor") -- --- _TIMERDISPATCHER:RemoveSchedule( self.CallID ) +-- _SCHEDULEDISPATCHER:RemoveSchedule( self.CallID ) --end --- Schedule a new time event. Note that the schedule will only take place if the scheduler is *started*. Even for a single schedule event, the scheduler needs to be started also. @@ -79,7 +91,7 @@ end -- @param #number RepeatSecondsInterval Specifies the interval in seconds when the scheduler will call the event function. -- @param #number RandomizationFactor Specifies a randomization factor between 0 and 1 to randomize the RepeatSecondsInterval. -- @param #number StopSeconds Specifies the amount of seconds when the scheduler will be stopped. --- @return #SCHEDULER self +-- @return #number The ScheduleID of the planned schedule. function SCHEDULER:Schedule( TimeEventObject, TimeEventFunction, TimeEventFunctionArguments, StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds ) self:F2( { StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds } ) self:T3( { TimeEventFunctionArguments } ) @@ -87,7 +99,7 @@ function SCHEDULER:Schedule( TimeEventObject, TimeEventFunction, TimeEventFuncti self.TimeEventObject = TimeEventObject - self.Schedules[#self.Schedules+1] = _TIMERDISPATCHER:AddSchedule( + local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( self, TimeEventFunction, TimeEventFunctionArguments, @@ -96,51 +108,37 @@ function SCHEDULER:Schedule( TimeEventObject, TimeEventFunction, TimeEventFuncti RandomizationFactor, StopSeconds ) - - return self -end - ---- (Re-)Starts the scheduler. --- @param #SCHEDULER self --- @return #SCHEDULER self -function SCHEDULER:Start() - self:F2() - - if self.RepeatSecondsInterval ~= 0 then - self.Repeat = true - end - if self.StartSeconds then - self:T( { self.StartSeconds } ) - self.Schedules[#self.Schedules+1] = _TIMERDISPATCHER:AddSchedule( - self, - self.TimeEventObject, - self.TimeEventFunction, - self.TimeEventFunctionArguments, - self.StartSeconds, - self.RepeatSecondsInterval, - self.RandomizationFactor, - self.StopSeconds - ) - end - - return self + self.Schedules[#self.Schedules+1] = ScheduleID + + return ScheduleID end ---- Stops the scheduler. +--- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided. -- @param #SCHEDULER self --- @return #SCHEDULER self -function SCHEDULER:Stop() - self:F2( self.TimeEventObject ) +-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. +function SCHEDULER:Start( ScheduleID ) + self:F3( { ScheduleID } ) - self.Repeat = false - if self.ScheduleID then - self:E( "Stop Schedule" ) - timer.removeFunction( self.ScheduleID ) - end - self.ScheduleID = nil + _SCHEDULEDISPATCHER:Start( self, ScheduleID ) +end - return self +--- Stops the schedules or a specific schedule if a valid ScheduleID is provided. +-- @param #SCHEDULER self +-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. +function SCHEDULER:Stop( ScheduleID ) + self:F3( { ScheduleID } ) + + _SCHEDULEDISPATCHER:Stop( self, ScheduleID ) +end + +--- Removes a specific schedule if a valid ScheduleID is provided. +-- @param #SCHEDULER self +-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. +function SCHEDULER:Remove( ScheduleID ) + self:F3( { ScheduleID } ) + + _SCHEDULEDISPATCHER:Remove( self, ScheduleID ) end diff --git a/Moose Development/Moose/Fsm/Fsm.lua b/Moose Development/Moose/Fsm/Fsm.lua index 373b544c8..d7a894a70 100644 --- a/Moose Development/Moose/Fsm/Fsm.lua +++ b/Moose Development/Moose/Fsm/Fsm.lua @@ -58,6 +58,8 @@ do -- FSM self._EndStates = {} self._Scores = {} + self.CallScheduler = SCHEDULER:New( self ) + return self end @@ -313,7 +315,8 @@ do -- FSM self:E( { EventName = EventName } ) return function( self, DelaySeconds, ... ) self:T( "Delayed Event: " .. EventName ) - SCHEDULER:New( self, self._handler, { EventName, ... }, DelaySeconds ) + local CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1 ) + self:T( { CallID = CallID } ) end end diff --git a/Moose Development/Moose/Moose.lua b/Moose Development/Moose/Moose.lua index b14d40bea..3dc6792af 100644 --- a/Moose Development/Moose/Moose.lua +++ b/Moose Development/Moose/Moose.lua @@ -7,7 +7,7 @@ Include.File( "Utilities/Utils" ) --- Core Classes Include.File( "Core/Base" ) Include.File( "Core/Scheduler" ) -Include.File( "Core/Timer") +Include.File( "Core/ScheduleDispatcher") Include.File( "Core/Event" ) Include.File( "Core/Menu" ) Include.File( "Core/Zone" ) @@ -64,8 +64,8 @@ Include.File( "Tasking/Task_A2G" ) --- Declare the event dispatcher based on the EVENT class _EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT ---- Declare the timer dispatcher based on the TIMER class -_TIMERDISPATCHER = TIMER:New() -- Core.Timer#TIMER +--- Declare the timer dispatcher based on the SCHEDULEDISPATCHER class +_SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER --- Declare the main database object, which is used internally by the MOOSE classes. _DATABASE = DATABASE:New() -- Database#DATABASE diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 983a85d1d..f599dd00b 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -539,6 +539,7 @@ function TASK_BASE:RemoveStateMachine( TaskUnit ) self.Fsm[TaskUnit] = nil collectgarbage() + self:T( "Garbage Collected, Processes should be finalized now ...") end --- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} diff --git a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz index d4b4e7d27..1f2126a2b 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.lua b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.lua index 852a01bdc..f5e5dab2d 100644 --- a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.lua +++ b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.lua @@ -42,24 +42,19 @@ end do local Test1 = TEST_BASE:New( "Hello World Test 1" ) -collectgarbage() Test1 = nil -collectgarbage() BASE:E( Test1 ) end -collectgarbage() local Test2 = TEST_BASE:New( "Hello World Test 2" ) -collectgarbage() BASE:E( Test2 ) local Test3 = TEST_BASE:New( "Hello World Test 3" ) Test3 = nil +BASE:E( Test3 ) collectgarbage() -BASE:E( Test3 ) - BASE:E( "Collect Garbage executed." ) BASE:E( "You should only see a Hello Worlld message for Test 2!" ) BASE:E( "Check if Test 1 and Test 3 are garbage collected!" ) \ No newline at end of file diff --git a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz index 2cdac6499..f0b44f713 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz differ diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz index fb792174b..15ec24d93 100644 Binary files a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz and b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz differ