diff --git a/Scripts/Events/BehaviourResult.cs b/Scripts/Events/BehaviourResult.cs index efd3281..80dc203 100755 --- a/Scripts/Events/BehaviourResult.cs +++ b/Scripts/Events/BehaviourResult.cs @@ -9,61 +9,106 @@ public struct BehaviourResult where TEvent : class, IEvent { /// - /// Reserved time constant - the behaviour should continue being processed. + /// Behaviour state option. + /// The names should be self explanatory. /// - const float TIME_CONTINUE = -1f; + public enum State + { + IGNORE = 0x00, + CONTINUE = 0x01, + SCHEDULE = 0x02, + RESCHEDULE = 0x04, + UNSCHEDULE = 0x08, + RESET = 0x10, + } + + /// + /// States allowed for event returning this result. + /// + public const State ALLOWED_SELF_STATES = State.CONTINUE | State.RESCHEDULE | State.UNSCHEDULE | State.RESET; + + /// + /// States allowed for events referenced by this result. + /// + public const State ALLOWED_REFERENCED_STATES = State.SCHEDULE | State.UNSCHEDULE; /// - /// Reserved time constant - the event should be removed from the simulation. + /// Behaviour state of the event returning this result. /// - const float TIME_UNSCHEDULE = -2f; + public State SelfState { get; internal set; } /// /// Minimum amount of time to pass before running the event's behaviour again. /// - /// - /// Values <0 are reserved for other result states. - /// - public float RescheduleTime { get; internal set; } + public float SelfTime { get; internal set; } /// - /// Behaviour iterator should be reset before its next run. + /// Behaviour state of the event referenced by this result. /// - public bool ResetBehaviour { get; internal set; } + public State ReferencedState { get; internal set; } /// - /// Simulation should continue processing the event's behaviour. + /// Event to be added to the simulation. /// - public bool ContinueBehaviour { get => RescheduleTime == TIME_CONTINUE; } + public TEvent ReferencedEvent { get; internal set; } /// - /// Simulation should now remove the event from the simulation. + /// Minimum amount of time to pass before running the event. /// - public bool UnscheduleEvent { get => RescheduleTime == TIME_UNSCHEDULE; } + public float ReferencedTime { get; internal set; } /// - /// Simulation should add a new event to the simulation. + /// Event retuning this result wants to continue processing its behaviour. /// - public bool ScheduleNewEvent { get => NewEvent != null; } + public bool ContinueBehaviour { get => (SelfState & State.CONTINUE) != State.IGNORE; } /// - /// Event to be added to the simulation. + /// Event retuning this result wants to reset its behaviour. /// - public TEvent NewEvent { get; internal set; } + public bool ResetBehaviour { get => (SelfState & State.RESET) != State.IGNORE; } /// - /// Minimum amount of time to pass before running the event. + /// Event retuning this result wants to reschedule itself. + /// + public bool RescheduleEvent { get => (SelfState & State.RESCHEDULE) != State.IGNORE; } + + /// + /// Event retuning this result wants to unschedule itself. + /// + public bool UnscheduleEvent { get => (SelfState & State.UNSCHEDULE) != State.IGNORE; } + + /// + /// Event retuning this result wants to schedule event it is referencing. + /// + public bool ScheduleReferenced { get => (ReferencedState & State.SCHEDULE) != State.IGNORE; } + + /// + /// Event retuning this result wants to unschedule event it is referencing. /// - public float NewEventScheduleTime { get; internal set; } + public bool UnscheduleReferenced { get => (ReferencedState & State.UNSCHEDULE) != State.IGNORE; } - BehaviourResult(float rescheduleTime, bool reset, TEvent @event = null, float scheduleTime = 0f) + BehaviourResult(State selfState, float selfTime = 0f, State referencedState = State.IGNORE, TEvent referencedEvent = null, float referencedTime = 0f) { - RescheduleTime = rescheduleTime; - ResetBehaviour = reset; - NewEvent = @event; - NewEventScheduleTime = scheduleTime; + selfState &= ALLOWED_SELF_STATES; + referencedState &= ALLOWED_REFERENCED_STATES; + + SelfState = selfState; + SelfTime = selfTime; + ReferencedState = referencedState; + ReferencedEvent = referencedEvent; + ReferencedTime = referencedTime; } + /// + /// Processing of the event's behaviour should continue immediately + /// or be reset and continue immediately (if is True). + /// + /// + /// Should behaviour iterator be reset? + /// Behaviour result instance reflecting continuing + public static BehaviourResult Continue(bool reset = false) + => new BehaviourResult(State.CONTINUE | (reset ? State.RESET : State.IGNORE)); + /// /// Event should be rescheduled and its behaviour run after minimum of seconds /// or be reset and rescheduled (if is True). @@ -74,10 +119,10 @@ public struct BehaviourResult /// Behaviour result instance reflecting rescheduling public static BehaviourResult Reschedule(float rescheduleTime, bool reset = true) { - if (rescheduleTime < 0f) - throw new ArgumentException("Event's reschedule time cannot be less than 0."); + if (rescheduleTime <= 0f) + throw new ArgumentException("Event's reschedule time cannot be less than or equal to 0."); - return new BehaviourResult(rescheduleTime, reset); + return new BehaviourResult(State.RESCHEDULE | (reset ? State.RESET : State.IGNORE), rescheduleTime); } /// @@ -86,55 +131,32 @@ public static BehaviourResult Reschedule(float rescheduleTime, boo /// /// Behaviour result instance reflecting unscheduling public static BehaviourResult Unschedule() - => new BehaviourResult(TIME_UNSCHEDULE, false); - - /// - /// Processing of the event's behaviour should continue immediately - /// or be reset and continue immediately (if is True). - /// - /// - /// Should behaviour iterator be reset? - /// Behaviour result instance reflecting continuing - public static BehaviourResult Continue(bool reset = false) - => new BehaviourResult(TIME_CONTINUE, reset); + => new BehaviourResult(State.UNSCHEDULE); /// - /// Processing of the event's behaviour should continue after scheduling a new - /// or be reset after scheduling the event (if is True). + /// Event should be rescheduled and its behaviour run after minimum of seconds + /// or be reset and rescheduled (if is True). + /// All that after scheduling a new (with that event waiting for a minimum of seconds). /// /// + /// Minimum amount of time to skip /// Event to be scheduled /// Event to be scheduled - /// Should behaviour iterator be reset? + /// Should behaviour iterator be reset? /// Behaviour result instance reflecting new event scheduling - public static BehaviourResult ScheduleNewAndContinue(TEvent @event, float scheduleTime = 0, bool reset = false) - => ScheduleNew(TIME_CONTINUE, @event, scheduleTime, reset); + public static BehaviourResult ScheduleNew(float rescheduleTime, TEvent @event, float scheduleTime = 0f, bool selfReset = false) + => new BehaviourResult(State.RESCHEDULE | (selfReset ? State.RESET : State.IGNORE), rescheduleTime, State.SCHEDULE, @event, scheduleTime); /// - /// Event should be rescheduled and its behaviour run after minimum of seconds - /// or be reset and rescheduled (if is True). - /// All that after scheduling a new (with that event waiting for a minimum of seconds). + /// Processing of the event's behaviour should continue after scheduling a new + /// or be reset after scheduling the event (if is True). /// /// - /// Minimum amount of time to skip /// Event to be scheduled /// Event to be scheduled - /// Should behaviour iterator be reset? + /// Should behaviour iterator be reset? /// Behaviour result instance reflecting new event scheduling - public static BehaviourResult ScheduleNew(float rescheduleTime, TEvent @event, float scheduleTime = 0, bool reset = false) - => new BehaviourResult(rescheduleTime, reset, @event, scheduleTime); - - public override string ToString() - { - if (ContinueBehaviour) - return "EventBehaviour: Continue" + (ResetBehaviour ? " and Reset" : ""); - - if (UnscheduleEvent) - return "EventBehaviour: Unschedule"; - - return "EventBehaviour: Reschedule in " + RescheduleTime + (ResetBehaviour ? " and Reset" : ""); - } - - public static implicit operator float(BehaviourResult result) => result.RescheduleTime; + public static BehaviourResult ScheduleNewAndContinue(TEvent @event, float scheduleTime = 0f, bool selfReset = false) + => new BehaviourResult(State.CONTINUE | (selfReset ? State.RESET : State.IGNORE), 0f, State.SCHEDULE, @event, scheduleTime); } } diff --git a/Scripts/Events/EventBase.cs b/Scripts/Events/EventBase.cs index d9ad52c..4e40a0d 100755 --- a/Scripts/Events/EventBase.cs +++ b/Scripts/Events/EventBase.cs @@ -7,12 +7,16 @@ namespace UnityDES.Events /// Base class for any simulation event. /// /// + /// Type of the derived event /// Type of the queue key public abstract class EventBase : IEvent where TEvent : class, IEvent { public TKey QueueKey { get; set; } + /// + /// Should always be current simulation time during processing the event's behaviour. + /// public TKey SimulationTime { get => QueueKey; } public IEnumerator> BehaviourCycle { get; protected set; } @@ -53,44 +57,47 @@ public void Run(ISimulationController simulationController) unfinished = BehaviourCycle.MoveNext(); behaviourResult = BehaviourCycle.Current; - if (behaviourResult.ScheduleNewEvent) + if (behaviourResult.ScheduleReferenced) { - simulationController.Schedule(behaviourResult.NewEvent, behaviourResult.NewEventScheduleTime); + // the currently processed event wants to schedule new referenced event + simulationController.Schedule(behaviourResult.ReferencedEvent, behaviourResult.ReferencedTime); + } + else if (behaviourResult.UnscheduleReferenced) + { + // the currently processed event wants to unschedule referenced event + simulationController.Unschedule(behaviourResult.ReferencedEvent); + } + + if (behaviourResult.ResetBehaviour) + { + // the currently processed event wants its to be reset + BehaviourCycle = Behaviour(); } } while (unfinished && behaviourResult.ContinueBehaviour); - if (unfinished && !behaviourResult.UnscheduleEvent) + if (unfinished && behaviourResult.RescheduleEvent) { // update the event's key (must be done before rescheduling!) - IncreaseKey(behaviourResult.RescheduleTime); + IncreaseKey(behaviourResult.SelfTime); // reschedule the event if (!simulationController.Reschedule(This())) throw new ApplicationException("Rescheduling of existing event has failed!"); - - if (behaviourResult.ResetBehaviour) - { - // create new behaviour iterator instance - BehaviourCycle = Behaviour(); - } } - else + else if (!unfinished || behaviourResult.UnscheduleEvent) { // the event's behaviour either completely finished or it voluntarily wants to be unscheduled // remove the event from the simulation - simulationController.Unschedule(This()); + if (!simulationController.Unschedule(This())) + throw new ApplicationException("Unscheduling of existing event has failed!"); + } + else + { + throw new ApplicationException("This should never happen."); } } - /// - protected BehaviourResult ScheduleNew(float rescheduleTime, TEvent @event, float scheduleTime, bool reset = false) - => BehaviourResult.ScheduleNew(rescheduleTime, @event, scheduleTime, reset); - - /// - protected BehaviourResult ScheduleNewAndContinue(TEvent @event, float scheduleTime, bool reset = false) - => BehaviourResult.ScheduleNewAndContinue(@event, scheduleTime, reset); - /// protected BehaviourResult Continue(bool reset = false) => BehaviourResult.Continue(reset); @@ -102,5 +109,13 @@ protected BehaviourResult Reschedule(float rescheduleTime, bool re /// protected BehaviourResult Unschedule() => BehaviourResult.Unschedule(); + + /// + protected BehaviourResult ScheduleNew(float rescheduleTime, TEvent @event, float scheduleTime, bool reset = false) + => BehaviourResult.ScheduleNew(rescheduleTime, @event, scheduleTime, reset); + + /// + protected BehaviourResult ScheduleNewAndContinue(TEvent @event, float scheduleTime, bool reset = false) + => BehaviourResult.ScheduleNewAndContinue(@event, scheduleTime, reset); } } diff --git a/Scripts/Events/IEvent.cs b/Scripts/Events/IEvent.cs index 400f7bf..c26a21f 100755 --- a/Scripts/Events/IEvent.cs +++ b/Scripts/Events/IEvent.cs @@ -17,7 +17,8 @@ public interface IEvent : IQueueItem public IEnumerator> BehaviourCycle { get; } /// - /// Runs the behaviour of the event and manages immediate rescheduling through the simulation instance. + /// Runs the behaviour of the event and manages its immediate rescheduling through the simulation instance. + /// It also processes any simulation requests the event's behaviour could have - schedule/unschedule referenced event, etc. /// void Run(ISimulationController simulationController); @@ -26,11 +27,7 @@ public interface IEvent : IQueueItem /// Its logic can be split into multiple stages using yield. /// /// - /// - /// Null value will remove the event from the queue. - /// 0 value will skip the yield and continue processing the behaviour. - /// Value >0 will reschedule the event accordingly always of minimum 1 tick ahead. - /// + /// describing what the event wants to do public abstract IEnumerator> Behaviour(); } } diff --git a/Tests/Events/BehaviourResultTests.cs b/Tests/Events/BehaviourResultTests.cs index 32c47fc..0685aad 100755 --- a/Tests/Events/BehaviourResultTests.cs +++ b/Tests/Events/BehaviourResultTests.cs @@ -27,19 +27,17 @@ public void Continue() result = BehaviourResult.Continue(); Assert.IsTrue(result.ContinueBehaviour); - Assert.IsFalse(result.ScheduleNewEvent); + Assert.IsFalse(result.ScheduleReferenced); Assert.IsFalse(result.UnscheduleEvent); Assert.IsFalse(result.ResetBehaviour); - Assert.IsNull(result.NewEvent); - Assert.IsTrue(result.RescheduleTime < 0); + Assert.IsNull(result.ReferencedEvent); result = BehaviourResult.Continue(true); Assert.IsTrue(result.ContinueBehaviour); - Assert.IsFalse(result.ScheduleNewEvent); + Assert.IsFalse(result.ScheduleReferenced); Assert.IsFalse(result.UnscheduleEvent); Assert.IsTrue(result.ResetBehaviour); - Assert.IsNull(result.NewEvent); - Assert.IsTrue(result.RescheduleTime < 0); + Assert.IsNull(result.ReferencedEvent); } [Test] @@ -49,19 +47,19 @@ public void Reschedule() result = BehaviourResult.Reschedule(1f, false); Assert.IsFalse(result.ContinueBehaviour); - Assert.IsFalse(result.ScheduleNewEvent); + Assert.IsFalse(result.ScheduleReferenced); Assert.IsFalse(result.UnscheduleEvent); Assert.IsFalse(result.ResetBehaviour); - Assert.IsNull(result.NewEvent); - Assert.AreEqual(1f, result.RescheduleTime); + Assert.IsNull(result.ReferencedEvent); + Assert.AreEqual(1f, result.SelfTime); result = BehaviourResult.Reschedule(1f); Assert.IsFalse(result.ContinueBehaviour); - Assert.IsFalse(result.ScheduleNewEvent); + Assert.IsFalse(result.ScheduleReferenced); Assert.IsFalse(result.UnscheduleEvent); Assert.IsTrue(result.ResetBehaviour); - Assert.IsNull(result.NewEvent); - Assert.AreEqual(1f, result.RescheduleTime); + Assert.IsNull(result.ReferencedEvent); + Assert.AreEqual(1f, result.SelfTime); } [Test] @@ -71,61 +69,58 @@ public void Unschedule() result = BehaviourResult.Unschedule(); Assert.IsFalse(result.ContinueBehaviour); - Assert.IsFalse(result.ScheduleNewEvent); + Assert.IsFalse(result.ScheduleReferenced); Assert.IsTrue(result.UnscheduleEvent); Assert.IsFalse(result.ResetBehaviour); - Assert.IsNull(result.NewEvent); - Assert.IsTrue(result.RescheduleTime < 0); + Assert.IsNull(result.ReferencedEvent); } [Test] public void ScheduleNew() { BehaviourResult result; - var newEvent = new PublicTestEvent(); + var ReferencedEvent = new PublicTestEvent(); - result = BehaviourResult.ScheduleNew(1f, newEvent, 2f); + result = BehaviourResult.ScheduleNew(1f, ReferencedEvent, 2f); Assert.IsFalse(result.ContinueBehaviour); - Assert.IsTrue(result.ScheduleNewEvent); + Assert.IsTrue(result.ScheduleReferenced); Assert.IsFalse(result.UnscheduleEvent); Assert.IsFalse(result.ResetBehaviour); - Assert.AreSame(newEvent, result.NewEvent); - Assert.AreEqual(2f, result.NewEventScheduleTime); - Assert.AreEqual(1f, result.RescheduleTime); + Assert.AreSame(ReferencedEvent, result.ReferencedEvent); + Assert.AreEqual(2f, result.ReferencedTime); + Assert.AreEqual(1f, result.SelfTime); - result = BehaviourResult.ScheduleNew(1f, newEvent, 2f, true); + result = BehaviourResult.ScheduleNew(1f, ReferencedEvent, 2f, true); Assert.IsFalse(result.ContinueBehaviour); - Assert.IsTrue(result.ScheduleNewEvent); + Assert.IsTrue(result.ScheduleReferenced); Assert.IsFalse(result.UnscheduleEvent); Assert.IsTrue(result.ResetBehaviour); - Assert.AreSame(newEvent, result.NewEvent); - Assert.AreEqual(2f, result.NewEventScheduleTime); - Assert.AreEqual(1f, result.RescheduleTime); + Assert.AreSame(ReferencedEvent, result.ReferencedEvent); + Assert.AreEqual(2f, result.ReferencedTime); + Assert.AreEqual(1f, result.SelfTime); } [Test] public void ScheduleNewAndContinue() { BehaviourResult result; - var newEvent = new PublicTestEvent(); + var ReferencedEvent = new PublicTestEvent(); - result = BehaviourResult.ScheduleNewAndContinue(newEvent, 2f); + result = BehaviourResult.ScheduleNewAndContinue(ReferencedEvent, 2f); Assert.IsTrue(result.ContinueBehaviour); - Assert.IsTrue(result.ScheduleNewEvent); + Assert.IsTrue(result.ScheduleReferenced); Assert.IsFalse(result.UnscheduleEvent); Assert.IsFalse(result.ResetBehaviour); - Assert.AreSame(newEvent, result.NewEvent); - Assert.AreEqual(2f, result.NewEventScheduleTime); - Assert.IsTrue(result.RescheduleTime < 0); + Assert.AreSame(ReferencedEvent, result.ReferencedEvent); + Assert.AreEqual(2f, result.ReferencedTime); - result = BehaviourResult.ScheduleNewAndContinue(newEvent, 2f, true); + result = BehaviourResult.ScheduleNewAndContinue(ReferencedEvent, 2f, true); Assert.IsTrue(result.ContinueBehaviour); - Assert.IsTrue(result.ScheduleNewEvent); + Assert.IsTrue(result.ScheduleReferenced); Assert.IsFalse(result.UnscheduleEvent); Assert.IsTrue(result.ResetBehaviour); - Assert.AreSame(newEvent, result.NewEvent); - Assert.AreEqual(2f, result.NewEventScheduleTime); - Assert.IsTrue(result.RescheduleTime < 0); + Assert.AreSame(ReferencedEvent, result.ReferencedEvent); + Assert.AreEqual(2f, result.ReferencedTime); } } } \ No newline at end of file diff --git a/Tests/Events/EventTests.cs b/Tests/Events/EventTests.cs index 18944e9..565b93b 100755 --- a/Tests/Events/EventTests.cs +++ b/Tests/Events/EventTests.cs @@ -85,41 +85,37 @@ public void Behaviour() var iterator = @event.Behaviour(); Assert.AreEqual(0, @event.State); - Assert.IsTrue(iterator.Current.RescheduleTime == 0); Assert.IsFalse(iterator.Current.ContinueBehaviour); - Assert.IsFalse(iterator.Current.ScheduleNewEvent); + Assert.IsFalse(iterator.Current.ScheduleReferenced); Assert.IsFalse(iterator.Current.UnscheduleEvent); Assert.IsFalse(iterator.Current.ResetBehaviour); Assert.IsTrue(iterator.MoveNext()); Assert.AreEqual(1, @event.State); - Assert.AreEqual(.25f, iterator.Current.RescheduleTime); + Assert.AreEqual(.25f, iterator.Current.SelfTime); Assert.IsFalse(iterator.Current.ContinueBehaviour); - Assert.IsFalse(iterator.Current.ScheduleNewEvent); + Assert.IsFalse(iterator.Current.ScheduleReferenced); Assert.IsFalse(iterator.Current.UnscheduleEvent); Assert.IsFalse(iterator.Current.ResetBehaviour); Assert.IsTrue(iterator.MoveNext()); Assert.AreEqual(2, @event.State); - Assert.IsTrue(iterator.Current.RescheduleTime <= 0); Assert.IsTrue(iterator.Current.ContinueBehaviour); - Assert.IsFalse(iterator.Current.ScheduleNewEvent); + Assert.IsFalse(iterator.Current.ScheduleReferenced); Assert.IsFalse(iterator.Current.UnscheduleEvent); Assert.IsFalse(iterator.Current.ResetBehaviour); Assert.IsTrue(iterator.MoveNext()); Assert.AreEqual(3, @event.State); - Assert.IsTrue(iterator.Current.RescheduleTime <= 0); Assert.IsTrue(iterator.Current.ContinueBehaviour); - Assert.IsTrue(iterator.Current.ScheduleNewEvent); + Assert.IsTrue(iterator.Current.ScheduleReferenced); Assert.IsFalse(iterator.Current.UnscheduleEvent); Assert.IsFalse(iterator.Current.ResetBehaviour); Assert.IsTrue(iterator.MoveNext()); Assert.AreEqual(4, @event.State); - Assert.IsTrue(iterator.Current.RescheduleTime <= 0); Assert.IsFalse(iterator.Current.ContinueBehaviour); - Assert.IsFalse(iterator.Current.ScheduleNewEvent); + Assert.IsFalse(iterator.Current.ScheduleReferenced); Assert.IsTrue(iterator.Current.UnscheduleEvent); Assert.IsFalse(iterator.Current.ResetBehaviour);