Recreated RSLatchExample in Playground
[Mograsim.git] / era.mi / src / era / mi / logic / timeline / Timeline.java
1 package era.mi.logic.timeline;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.PriorityQueue;
6 import java.util.function.Consumer;
7
8 /**
9  * Orders Events by the time they are due to be executed. Can execute Events individually.
10  * 
11  * @author Fabian Stemmler
12  *
13  */
14 public class Timeline
15 {
16         private PriorityQueue<InnerEvent> events;
17         private long currentTime = 0;
18
19         private final List<Consumer<TimelineEvent>> eventAddedListener;
20
21         public Timeline(int initCapacity)
22         {
23                 events = new PriorityQueue<InnerEvent>(initCapacity);
24
25                 eventAddedListener = new ArrayList<>();
26         }
27
28         public boolean hasNext()
29         {
30                 return !events.isEmpty();
31         }
32
33         public void executeNext()
34         {
35                 InnerEvent first = events.peek();
36                 if (first != null)
37                         executeUpTo(first.getTiming(), -1);
38         }
39
40         public void executeAll()
41         {
42                 while (hasNext())
43                         executeNext();
44         }
45
46         /**
47          * Executes all events up to a given simulation timestamp. The simulation process can be constrained by a real world timestamp.
48          * 
49          * @param timestamp  the simulation timestamp up to which the events will be processed
50          * @param stopMillis the System.currentTimeMillis() when simulation definitely needs to stop. A value of -1 means no timeout.
51          * @return if it was possible to fulfill the goal in the given real world time.
52          * @author Christian Femers
53          */
54         public ExecutionResult executeUpTo(long timestamp, long stopMillis)
55         {
56                 if (events.isEmpty())
57                 {
58                         currentTime = timestamp;
59                         return ExecutionResult.NOTHING_DONE;
60                 }
61                 int checkStop = 0;
62                 InnerEvent first = events.peek();
63                 while (first != null && first.getTiming() <= timestamp)
64                 {
65                         events.remove();
66                         currentTime = first.getTiming();
67                         first.run();
68                         // Don't check after every run
69                         checkStop = (checkStop + 1) % 10;
70                         if (checkStop == 0 && System.currentTimeMillis() >= stopMillis)
71                                 return ExecutionResult.RAN_OUT_OF_TIME;
72                         first = events.peek();
73                 }
74                 currentTime = timestamp;
75                 return ExecutionResult.DONE_IN_TIME;
76         }
77
78         public long getSimulationTime()
79         {
80                 return currentTime;
81         }
82
83         public long nextEventTime()
84         {
85                 if (!hasNext())
86                         return -1;
87                 return events.peek().getTiming();
88         }
89
90         public void reset()
91         {
92                 events.clear();
93                 currentTime = 0;
94         }
95
96         public void addEventAddedListener(Consumer<TimelineEvent> listener)
97         {
98                 eventAddedListener.add(listener);
99         }
100
101         public void removeEventAddedListener(Consumer<TimelineEvent> listener)
102         {
103                 eventAddedListener.remove(listener);
104         }
105
106         /**
107          * Adds an Event to the {@link Timeline}
108          * 
109          * @param function       The {@link TimelineEventHandler} that will be executed, when the {@link InnerEvent} occurs on the timeline.
110          * @param relativeTiming The amount of MI ticks in which the {@link InnerEvent} is called, starting from the current time.
111          */
112         public void addEvent(TimelineEventHandler function, int relativeTiming)
113         {
114                 long timing = currentTime + relativeTiming;
115                 TimelineEvent event = new TimelineEvent(timing);
116                 events.add(new InnerEvent(function, event));
117                 eventAddedListener.forEach(l -> l.accept(event));
118         }
119
120         private class InnerEvent implements Runnable, Comparable<InnerEvent>
121         {
122                 private final TimelineEventHandler function;
123                 private final TimelineEvent event;
124
125                 /**
126                  * Creates an {@link InnerEvent}
127                  * 
128                  * @param function {@link TimelineEventHandler} to be executed when the {@link InnerEvent} occurs
129                  * @param timing   Point in the MI simulation {@link Timeline}, at which the {@link InnerEvent} is executed;
130                  */
131                 InnerEvent(TimelineEventHandler function, TimelineEvent event)
132                 {
133                         this.function = function;
134                         this.event = event;
135                 }
136
137                 public long getTiming()
138                 {
139                         return event.getTiming();
140                 }
141
142                 @Override
143                 public void run()
144                 {
145                         function.handle(event);
146                 }
147
148                 @Override
149                 public String toString()
150                 {
151                         return event.toString();
152                 }
153
154                 @Override
155                 public int compareTo(InnerEvent o)
156                 {
157                         long difference = getTiming() - o.getTiming();
158                         if (difference == 0)
159                                 return 0;
160                         return difference < 0 ? -1 : 1;
161                 }
162         }
163
164         @Override
165         public String toString()
166         {
167                 return "simulation time: " + currentTime + ", " + events.toString();
168         }
169
170         public static long toNanoseconds(long ticks)
171         {
172                 return ticks; // TODO: Alter this when it has been determined how ticks should relate to real time.
173         }
174
175         public enum ExecutionResult
176         {
177                 NOTHING_DONE, DONE_IN_TIME, RAN_OUT_OF_TIME
178         }
179 }