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