#region License Information /* SimSharp - A .NET port of SimPy, discrete event simulation framework Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see .*/ #endregion using System; using System.Collections.Generic; namespace SimSharp { /// /// The base class for all events in SimSharp. /// An event can be in one of three states at any time: /// - Alive: The event object exists, but is neither scheduled to /// be executed, nor is it already executed. /// - Triggered: The event has been put in the event queue and is /// going to be executed. /// - Processed: The event has been executed. /// /// Usually, the event is alive until its Trigger, Succeed, or Fail /// method have been called. Then it becomes triggered. When the /// Environment progresses to the event and executes its callbacks /// the event becomes processed. /// public class Event { protected internal Environment Environment { get; private set; } protected List> CallbackList { get; set; } /// /// The value property can be used to return arbitrary data from a /// process or an event. It also represents the interrupt cause to /// a process. /// public object Value { get; protected set; } /// /// The IsOk flag indicates if the event succeeded or failed. An event /// that failed indicates to a waiting process that the action could /// not be performed and that the faulting situation must be handled. /// Typically, interrupting a process sets the IsOk flag to false. /// public bool IsOk { get; protected set; } /// /// An event is alive when it is not triggered and not processed. That /// is, when it exists in memory without being scheduled. Typically, /// a Process is alive until its last event has been processed and the /// process event itself is to be processed. /// public bool IsAlive { get { return !IsTriggered && !IsProcessed; } } /// /// An event becomes processed when its callbacks have been executed. /// Events may only be processed once and an exception will be thrown /// if they are to be processed multiple times. /// public bool IsProcessed { get; protected set; } /// /// An event becomes triggered when it is placed into the event queue. /// That is, when its callbacks are going to be executed. /// An even that is triggered may later not be failed or retriggered. /// public bool IsTriggered { get; protected set; } public Event(Environment environment) { Environment = environment; CallbackList = new List>(); } /// /// This method schedules the event right now. It takes the IsOk state /// and uses the of the given . /// Thus if the given event fails, this event will also be triggered as /// failing. /// /// /// Thrown when the event has already been triggered. /// /// /// The signature of this method allows it to be used as a callback. /// /// The event that triggers this event. public virtual void Trigger(Event @event) { if (IsTriggered) throw new InvalidOperationException("Event has already been triggered."); IsOk = @event.IsOk; Value = @event.Value; IsTriggered = true; Environment.Schedule(this); } /// /// This method schedules the event right now. It sets IsOk state to true /// and optionally uses also the value. If urgent is given, the event may /// be scheduled as urgent. Urgent events are placed in a separate event /// queue. The callbacks of urgent events are executed before normal events. /// /// /// Thrown when the event has already been triggered. /// /// The value that the event should use. /// Whether the event should be scheduled urgently. /// This is ususally not required and should be reserved for very special /// cases. public virtual void Succeed(object value = null) { if (IsTriggered) throw new InvalidOperationException("Event has already been triggered."); IsOk = true; Value = value; IsTriggered = true; Environment.Schedule(this); } /// /// This method schedules the event right now. It sets IsOk state to false /// and optionally uses also the value. If urgent is given, the event may /// be scheduled as urgent. Urgent events are placed in a separate event /// queue. The callbacks of urgent events are executed before normal events. /// /// /// Thrown when the event has already been triggered. /// /// The value that the event should use. /// Whether the event should be scheduled urgently. /// This is ususally not required and should be reserved for very special /// cases. public virtual void Fail(object value = null) { if (IsTriggered) throw new InvalidOperationException("Event has already been triggered."); IsOk = false; Value = value; IsTriggered = true; Environment.Schedule(this); } /// /// This method adds a callback to the list of callbacks. Callbacks will be /// executed in the order they have been added. /// /// The callback to execute when the event is being /// processed. public virtual void AddCallback(Action callback) { if (IsProcessed) throw new InvalidOperationException("Event is already processed."); CallbackList.Add(callback); } /// /// This method adds a range of callbacks to the list of callbacks. Callbacks /// will be executed in the order they have been added. /// /// The callbacks to execute when the event is being /// processed. public virtual void AddCallbacks(IEnumerable> callbacks) { if (IsProcessed) throw new InvalidOperationException("Event is already processed."); CallbackList.AddRange(callbacks); } /// /// This method removes a callback to the list of callbacks. /// /// /// It is not checked if the callback has actually been added before and /// no exception will be thrown if it had not been present. /// /// The callback to remove. public virtual void RemoveCallback(Action callback) { if (IsProcessed) throw new InvalidOperationException("Event is already processed."); CallbackList.Remove(callback); } /// /// This method processes the event, that is, it calls all the callbacks. /// When it finishes it will be marked IsProcessed and cannot be processed /// again. /// /// When the event has already /// been processed. public virtual void Process() { if (IsProcessed) throw new InvalidOperationException("Event has already been processed."); IsProcessed = true; foreach (var callback in CallbackList) callback(this); CallbackList = null; } public static Condition operator &(Event event1, Event event2) { return new AllOf(event1.Environment, event1, event2); } public static Condition operator |(Event event1, Event event2) { return new AnyOf(event1.Environment, event1, event2); } } }