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