]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.simulation.sequences/src/org/simantics/simulation/sequences/action/AbstractActionContext.java
1187230ad12f4780914797193e4045ac613810ef
[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<>();
18     ArrayList<Function1<Tuple0, Object>> scheduledNextStep = new ArrayList<>();
19     ArrayList<Function1<StopReason, Object>> scheduledWhenStopped = new ArrayList<>();
20     PriorityQueue<Task> scheduledAt = new PriorityQueue<>();
21
22         public List<Exception> exceptions; 
23     
24     private static class Task implements Comparable<Task> {
25         final double time;
26         final Function1<Tuple0, Object> continuation;
27         
28         public Task(double time, Function1<Tuple0, Object> continuation) {
29             this.time = time;
30             this.continuation = continuation;
31         }
32
33         @Override
34         public int compareTo(Task o) {
35             return Double.compare(time, o.time);
36         }
37     }
38     
39     @Override
40     public double time() {
41         return currentTime;
42     }
43     
44     @Override
45     public void scheduleNow(Function1<Tuple0, Object> continuation) {
46         scheduledNow.add(continuation);
47     }
48     
49     @Override
50     public void scheduleNextStep(Function1<Tuple0, Object> continuation) {
51         scheduledNextStep.add(continuation);
52     }
53     
54     @Override
55     public void scheduleAt(double time, Function1<Tuple0, Object> continuation) {
56         if(time <= currentTime)
57             scheduleNow(continuation);
58         else
59             scheduledAt.add(new Task(time, continuation));
60     }
61
62     @Override
63     public void scheduleWhenStopped(Function1<StopReason, Object> continuation) {
64         scheduledWhenStopped.add(continuation);
65     }
66
67     @Override
68     public void stop() {
69         stop(StopReason.STOPPED);
70     }
71
72     public void stop(StopReason reason) {
73         stopped = true;
74         handleStop(reason);
75     }
76
77     public boolean isStopped() {
78         synchronized (this) {
79                 return stopped || (scheduledNextStep.isEmpty() && scheduledAt.isEmpty());
80         }
81     }
82     
83     public double handleStep(double currentTime) {
84         synchronized (this) {
85                 if (stopped)
86                         return Double.POSITIVE_INFINITY;
87                 
88                 this.currentTime = currentTime;
89                 {
90                     ArrayList<Function1<Tuple0, Object>> temp = scheduledNow;
91                     scheduledNow = scheduledNextStep;
92                     scheduledNextStep = temp;
93                     Collections.reverse(scheduledNow);
94                 }
95                         
96                 SCLContext context = SCLContext.getCurrent();
97                 Object oldActionContext = context.put("sequenceAction", this);
98                 try {
99                     Task firstTask = scheduledAt.peek();
100                     while(true) {
101                         while(!scheduledNow.isEmpty()) {
102                                 try {
103                                         Function1<Tuple0, Object> currentContinuation = scheduledNow.remove(scheduledNow.size()-1);
104                                     currentContinuation.apply(Tuple0.INSTANCE);
105                                         currentContinuation = null;
106                                 } catch (Exception e) {
107                                         if (this.exceptions == null)
108                                                 this.exceptions = new ArrayList<>();
109                                         this.exceptions.add(new RuntimeException("Action failure at " + currentTime + ": " + e.getMessage(), e));
110                                 }
111                         }
112                         if(firstTask == null)
113                             return Double.POSITIVE_INFINITY;
114                         else if(firstTask.time > currentTime+TIME_TOLERANCE)
115                             return firstTask.time;
116                         else {
117                             firstTask.continuation.apply(Tuple0.INSTANCE);
118                                 synchronized (this) {
119                                         scheduledAt.remove();
120                                 }
121                             firstTask = scheduledAt.peek();
122                         }
123                     }
124                 } finally {
125                     context.put("sequenceAction", oldActionContext);
126                 }
127         }
128     }
129
130     private void handleStop(StopReason reason) {
131         synchronized (this) {
132             List<Function1<StopReason, Object>> stopFunctions = new ArrayList<>(scheduledWhenStopped);
133             scheduledWhenStopped.clear();
134             
135             scheduledNextStep.clear();
136             scheduledAt.clear();
137
138             SCLContext context = SCLContext.getCurrent();
139             Object oldActionContext = context.put("sequenceAction", this);
140             try {
141                 stopFunctions.forEach(f -> {
142                     try {
143                         f.apply(reason);
144                     } catch (Exception e) {
145                         if (this.exceptions == null)
146                             this.exceptions = new ArrayList<>();
147                         this.exceptions.add(new RuntimeException("Stop action failure at " + currentTime + ": " + e.getMessage(), e));
148                     }
149                 });
150             } finally {
151                 context.put("sequenceAction", oldActionContext);
152             }
153         }
154     }
155 }