4fabd2243c98dedea07bb50de9dd9ac6b099270c
[simantics/platform.git] / bundles / org.simantics.simulation.sequences / src / org / simantics / simulation / sequences / action / AbstractActionContext.java
1 package org.simantics.simulation.sequences.action;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.PriorityQueue;
7
8 import org.simantics.scl.runtime.SCLContext;
9 import org.simantics.scl.runtime.function.Function1;
10 import org.simantics.scl.runtime.tuple.Tuple0;
11
12 public abstract class AbstractActionContext implements ActionContext {
13     public static final double TIME_TOLERANCE = 1e-6;
14     
15     double currentTime;
16     volatile boolean stopped;
17     ArrayList<Function1<Tuple0, Object>> scheduledNow = new ArrayList<Function1<Tuple0, Object>>();
18     ArrayList<Function1<Tuple0, Object>> scheduledNextStep = new ArrayList<Function1<Tuple0, Object>>();
19     PriorityQueue<Task> scheduledAt = new PriorityQueue<Task>();
20
21         public List<Exception> exceptions; 
22     
23     private static class Task implements Comparable<Task> {
24         final double time;
25         final Function1<Tuple0, Object> continuation;
26         
27         public Task(double time, Function1<Tuple0, Object> continuation) {
28             this.time = time;
29             this.continuation = continuation;
30         }
31
32         @Override
33         public int compareTo(Task o) {
34             return Double.compare(time, o.time);
35         }
36     }
37     
38     @Override
39     public double time() {
40         return currentTime;
41     }
42     
43     @Override
44     public void scheduleNow(Function1<Tuple0, Object> continuation) {
45         scheduledNow.add(continuation);
46     }
47     
48     @Override
49     public void scheduleNextStep(Function1<Tuple0, Object> continuation) {
50         scheduledNextStep.add(continuation);
51     }
52     
53     @Override
54     public void scheduleAt(double time, Function1<Tuple0, Object> continuation) {
55         if(time <= currentTime)
56             scheduleNow(continuation);
57         else
58             scheduledAt.add(new Task(time, continuation));
59     }
60     
61     @Override
62     public void stop() {
63         stopped = true;
64     }
65     
66     public boolean isStopped() {
67         synchronized (this) {
68                 return stopped || (scheduledNextStep.isEmpty() && scheduledAt.isEmpty());
69         }
70     }
71     
72     public double handleStep(double currentTime) {
73         synchronized (this) {
74                 this.currentTime = currentTime;
75                 {
76                     ArrayList<Function1<Tuple0, Object>> temp = scheduledNow;
77                     scheduledNow = scheduledNextStep;
78                     scheduledNextStep = temp;
79                     Collections.reverse(scheduledNow);
80                 }
81                         
82                 SCLContext context = SCLContext.getCurrent();
83                 Object oldActionContext = context.put("sequenceAction", this);
84                 try {
85                     Task firstTask = scheduledAt.peek();
86                     while(true) {
87                         while(!scheduledNow.isEmpty()) {
88                                 try {
89                                         Function1<Tuple0, Object> currentContinuation = scheduledNow.remove(scheduledNow.size()-1);
90                                     currentContinuation.apply(Tuple0.INSTANCE);
91                                         currentContinuation = null;
92                                 } catch (Exception e) {
93                                         if (this.exceptions == null)
94                                                 this.exceptions = new ArrayList<Exception>();
95                                         this.exceptions.add(new RuntimeException("Action failure at " + currentTime + ": " + e.getMessage(), e));
96                                 }
97                         }
98                         if(firstTask == null)
99                             return Double.POSITIVE_INFINITY;
100                         else if(firstTask.time > currentTime+TIME_TOLERANCE)
101                             return firstTask.time;
102                         else {
103                             firstTask.continuation.apply(Tuple0.INSTANCE);
104                                 synchronized (this) {
105                                         scheduledAt.remove();
106                                 }
107                             firstTask = scheduledAt.peek();
108                         }
109                     }
110                 } finally {
111                     context.put("sequenceAction", oldActionContext);
112                 }
113         }
114     }
115 }