#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);
}
}
}