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