Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.3.1/SimSharp-3.3.1/Core/Events/Process.cs @ 17487

Last change on this file since 17487 was 17487, checked in by abeham, 4 years ago

#3065: update Sim# to 3.3.1

File size: 7.7 KB
Line 
1#region License Information
2/*
3 * This file is part of SimSharp which is licensed under the MIT license.
4 * See the LICENSE file in the project root for more information.
5 */
6#endregion
7
8using System;
9using System.Collections.Generic;
10
11namespace SimSharp {
12  /// <summary>
13  /// A Process handles the iteration of events. Processes may define steps that
14  /// a certain entity in the simulation has to perform. Each time the process
15  /// should wait it yields an event and will be resumed when that event is processed.
16  /// </summary>
17  /// <remarks>
18  /// Since an iterator method does not have access to its process, the method can
19  /// retrieve the associated Process through the ActiveProcess property of the
20  /// environment. Each Process sets and resets that property during Resume.
21  /// </remarks>
22  public class Process : Event {
23    private readonly IEnumerator<Event> generator;
24    private Event target;
25    /// <summary>
26    /// Target is the event that is expected to be executed next in the process.
27    /// </summary>
28    public Event Target {
29      get { return target; }
30      protected set { target = value; }
31    }
32
33    /// <summary>
34    /// Sets up a new process.
35    /// The process places an initialize event into the event queue which starts
36    /// the process by retrieving events from the generator.
37    /// </summary>
38    /// <param name="environment">The environment in which the process lives.</param>
39    /// <param name="generator">The generator function of the process.</param>
40    /// <param name="priority">The priority if multiple processes are started at the same time.</param>
41    public Process(Simulation environment, IEnumerable<Event> generator, int priority = 0)
42      : base(environment) {
43      this.generator = generator.GetEnumerator();
44      IsOk = true;
45      target = new Initialize(environment, this, priority);
46    }
47
48    /// <summary>
49    /// This interrupts a process and causes the IsOk flag to be set to false.
50    /// If a process is interrupted the iterator method needs to call HandleFault()
51    /// before continuing to yield further events.
52    /// </summary>
53    /// <exception cref="InvalidOperationException">This is thrown in three conditions:
54    ///  - If the process has already been triggered.
55    ///  - If the process attempts to interrupt itself.
56    ///  - If the process continues to yield events despite being faulted.</exception>
57    /// <param name="cause">The cause of the interrupt.</param>
58    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
59    public virtual void Interrupt(object cause = null, int priority = 0) {
60      if (IsTriggered) throw new InvalidOperationException("The process has terminated and cannot be interrupted.");
61      if (Environment.ActiveProcess == this) throw new InvalidOperationException("A process is not allowed to interrupt itself.");
62
63      var interruptEvent = new Event(Environment);
64      interruptEvent.AddCallback(Resume);
65      interruptEvent.Fail(cause, priority);
66
67      if (Target != null)
68        Target.RemoveCallback(Resume);
69    }
70
71    protected virtual void Resume(Event @event) {
72      Environment.ActiveProcess = this;
73      while (true) {
74        if (@event.IsOk) {
75          if (generator.MoveNext()) {
76            if (IsTriggered) {
77              // the generator called e.g. Environment.ActiveProcess.Fail
78              Environment.ActiveProcess = null;
79              return;
80            }
81            if (!ProceedToEvent()) {
82              @event = target;
83              continue;
84            } else break;
85          } else if (!IsTriggered) {
86            Succeed(@event.Value);
87            break;
88          } else break;
89        } else {
90          /* Fault handling differs from SimPy as in .NET it is not possible to inject an
91         * exception into an enumerator and it is impossible to put a yield return inside
92         * a try-catch block. In SimSharp the Process will set IsOk and will then move to
93         * the next yield in the generator. However, if after this move IsOk is still false
94         * we know that the error was not handled. It is assumed the error is handled if
95         * HandleFault() is called on the environment's ActiveProcess which will reset the
96         * flag. */
97          IsOk = false;
98          Value = @event.Value;
99
100          if (generator.MoveNext()) {
101            if (IsTriggered) {
102              // the generator called e.g. Environment.ActiveProcess.Fail
103              Environment.ActiveProcess = null;
104              return;
105            }
106            // if we move next, but IsOk is still false
107            if (!IsOk) throw new InvalidOperationException("The process did not react to being faulted.");
108            // otherwise HandleFault was called and the fault was handled
109            if (ProceedToEvent()) break;
110          } else if (!IsTriggered) {
111            if (!IsOk) Fail(@event.Value);
112            else Succeed(@event.Value);
113            break;
114          } else break;
115        }
116      }
117      Environment.ActiveProcess = null;
118    }
119   
120    protected virtual bool ProceedToEvent() {
121      target = generator.Current;
122      Value = target.Value;
123      if (target.IsProcessed) return false;
124      target.AddCallback(Resume);
125      return true;
126    }
127
128    /// <summary>
129    /// This method must be called to reset the IsOk flag of the process back to true.
130    /// The IsOk flag may be set to false if the process waited on an event that failed.
131    /// </summary>
132    /// <remarks>
133    /// In SimPy a faulting process would throw an exception which is then catched and
134    /// chained. In SimSharp catching exceptions from a yield is not possible as a yield
135    /// return statement may not throw an exception.
136    /// If a processes faulted the Value property may indicate a cause for the fault.
137    /// </remarks>
138    /// <returns>True if a faulting situation needs to be handled, false if the process
139    /// is okay and the last yielded event succeeded.</returns>
140    public virtual bool HandleFault() {
141      if (IsOk) return false;
142      IsOk = true;
143      return true;
144    }
145
146    private class Initialize : Event {
147      public Initialize(Simulation environment, Process process, int priority)
148        : base(environment) {
149        CallbackList.Add(process.Resume);
150        IsOk = true;
151        IsTriggered = true;
152        environment.Schedule(this, priority);
153      }
154    }
155  }
156
157  public class PseudoRealtimeProcess : Process {
158    public double RealtimeScale { get; set; }
159
160    public new PseudoRealtimeSimulation Environment {
161      get { return (PseudoRealtimeSimulation)base.Environment; }
162    }
163
164    /// <summary>
165    /// Sets up a new process.
166    /// The process places an initialize event into the event queue which starts
167    /// the process by retrieving events from the generator.
168    /// </summary>
169    /// <param name="environment">The environment in which the process lives.</param>
170    /// <param name="generator">The generator function of the process.</param>
171    /// <param name="priority">The priority if multiple processes are started at the same time.</param>
172    /// <param name="realtimeScale">A value strictly greater than 0 used to scale real time events (1 = realtime).</param>
173    public PseudoRealtimeProcess(PseudoRealtimeSimulation environment, IEnumerable<Event> generator, int priority = 0, double realtimeScale = PseudoRealtimeSimulation.DefaultRealtimeScale)
174      : base(environment, generator, priority) {
175      RealtimeScale = realtimeScale;
176    }
177
178    protected override void Resume(Event @event) {
179      Environment.SetRealtime(RealtimeScale);
180      base.Resume(@event);
181    }
182  }
183}
Note: See TracBrowser for help on using the repository browser.