--- /dev/null
+package org.simantics.simulation.sequences.action;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.List;\r
+import java.util.PriorityQueue;\r
+\r
+import org.simantics.scl.runtime.SCLContext;\r
+import org.simantics.scl.runtime.function.Function1;\r
+import org.simantics.scl.runtime.tuple.Tuple0;\r
+\r
+public abstract class AbstractActionContext implements ActionContext {\r
+ public static final double TIME_TOLERANCE = 1e-6;\r
+ \r
+ double currentTime;\r
+ volatile boolean stopped;\r
+ ArrayList<Function1<Tuple0, Object>> scheduledNow = new ArrayList<Function1<Tuple0, Object>>();\r
+ ArrayList<Function1<Tuple0, Object>> scheduledNextStep = new ArrayList<Function1<Tuple0, Object>>();\r
+ PriorityQueue<Task> scheduledAt = new PriorityQueue<Task>();\r
+\r
+ public List<Exception> exceptions; \r
+ \r
+ private static class Task implements Comparable<Task> {\r
+ final double time;\r
+ final Function1<Tuple0, Object> continuation;\r
+ \r
+ public Task(double time, Function1<Tuple0, Object> continuation) {\r
+ this.time = time;\r
+ this.continuation = continuation;\r
+ }\r
+\r
+ @Override\r
+ public int compareTo(Task o) {\r
+ return Double.compare(time, o.time);\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public double time() {\r
+ return currentTime;\r
+ }\r
+ \r
+ @Override\r
+ public void scheduleNow(Function1<Tuple0, Object> continuation) {\r
+ scheduledNow.add(continuation);\r
+ }\r
+ \r
+ @Override\r
+ public void scheduleNextStep(Function1<Tuple0, Object> continuation) {\r
+ scheduledNextStep.add(continuation);\r
+ }\r
+ \r
+ @Override\r
+ public void scheduleAt(double time, Function1<Tuple0, Object> continuation) {\r
+ if(time <= currentTime)\r
+ scheduleNow(continuation);\r
+ else\r
+ scheduledAt.add(new Task(time, continuation));\r
+ }\r
+ \r
+ @Override\r
+ public void stop() {\r
+ stopped = true;\r
+ }\r
+ \r
+ public boolean isStopped() {\r
+ synchronized (this) {\r
+ return stopped || (scheduledNextStep.isEmpty() && scheduledAt.isEmpty());\r
+ }\r
+ }\r
+ \r
+ public double handleStep(double currentTime) {\r
+ synchronized (this) {\r
+ this.currentTime = currentTime;\r
+ {\r
+ ArrayList<Function1<Tuple0, Object>> temp = scheduledNow;\r
+ scheduledNow = scheduledNextStep;\r
+ scheduledNextStep = temp;\r
+ Collections.reverse(scheduledNow);\r
+ }\r
+ \r
+ SCLContext context = SCLContext.getCurrent();\r
+ Object oldActionContext = context.put("sequenceAction", this);\r
+ try {\r
+ Task firstTask = scheduledAt.peek();\r
+ while(true) {\r
+ while(!scheduledNow.isEmpty()) {\r
+ try {\r
+ Function1<Tuple0, Object> currentContinuation = scheduledNow.remove(scheduledNow.size()-1);\r
+ currentContinuation.apply(Tuple0.INSTANCE);\r
+ currentContinuation = null;\r
+ } catch (Exception e) {\r
+ if (this.exceptions == null)\r
+ this.exceptions = new ArrayList<Exception>();\r
+ this.exceptions.add(new RuntimeException("Action failure at " + currentTime + ": " + e.getMessage(), e));\r
+ }\r
+ }\r
+ if(firstTask == null)\r
+ return Double.POSITIVE_INFINITY;\r
+ else if(firstTask.time > currentTime+TIME_TOLERANCE)\r
+ return firstTask.time;\r
+ else {\r
+ firstTask.continuation.apply(Tuple0.INSTANCE);\r
+ synchronized (this) {\r
+ scheduledAt.remove();\r
+ }\r
+ firstTask = scheduledAt.peek();\r
+ }\r
+ }\r
+ } finally {\r
+ context.put("sequenceAction", oldActionContext);\r
+ }\r
+ }\r
+ }\r
+}\r