1 package org.simantics.simulation.sequences.action;
3 import java.util.ArrayList;
4 import java.util.Collections;
6 import java.util.PriorityQueue;
8 import org.simantics.scl.runtime.SCLContext;
9 import org.simantics.scl.runtime.function.Function1;
10 import org.simantics.scl.runtime.tuple.Tuple0;
12 public abstract class AbstractActionContext implements ActionContext {
13 public static final double TIME_TOLERANCE = 1e-6;
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<>();
22 public List<Exception> exceptions;
24 private static class Task implements Comparable<Task> {
26 final Function1<Tuple0, Object> continuation;
28 public Task(double time, Function1<Tuple0, Object> continuation) {
30 this.continuation = continuation;
34 public int compareTo(Task o) {
35 return Double.compare(time, o.time);
40 public double time() {
45 public void scheduleNow(Function1<Tuple0, Object> continuation) {
46 scheduledNow.add(continuation);
50 public void scheduleNextStep(Function1<Tuple0, Object> continuation) {
51 scheduledNextStep.add(continuation);
55 public void scheduleAt(double time, Function1<Tuple0, Object> continuation) {
56 if(time <= currentTime)
57 scheduleNow(continuation);
59 scheduledAt.add(new Task(time, continuation));
63 public void scheduleWhenStopped(Function1<StopReason, Object> continuation) {
64 scheduledWhenStopped.add(continuation);
69 stop(StopReason.STOPPED);
72 public void stop(StopReason reason) {
77 public boolean isStopped() {
79 return stopped || (scheduledNextStep.isEmpty() && scheduledAt.isEmpty());
83 public double handleStep(double currentTime) {
86 return Double.POSITIVE_INFINITY;
88 this.currentTime = currentTime;
90 ArrayList<Function1<Tuple0, Object>> temp = scheduledNow;
91 scheduledNow = scheduledNextStep;
92 scheduledNextStep = temp;
93 Collections.reverse(scheduledNow);
96 SCLContext context = SCLContext.getCurrent();
97 Object oldActionContext = context.put("sequenceAction", this);
99 Task firstTask = scheduledAt.peek();
101 while(!scheduledNow.isEmpty()) {
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));
112 if(firstTask == null)
113 return Double.POSITIVE_INFINITY;
114 else if(firstTask.time > currentTime+TIME_TOLERANCE)
115 return firstTask.time;
117 firstTask.continuation.apply(Tuple0.INSTANCE);
118 synchronized (this) {
119 scheduledAt.remove();
121 firstTask = scheduledAt.peek();
125 context.put("sequenceAction", oldActionContext);
130 private void handleStop(StopReason reason) {
131 synchronized (this) {
132 List<Function1<StopReason, Object>> stopFunctions = new ArrayList<>(scheduledWhenStopped);
133 scheduledWhenStopped.clear();
135 scheduledNextStep.clear();
138 SCLContext context = SCLContext.getCurrent();
139 Object oldActionContext = context.put("sequenceAction", this);
141 stopFunctions.forEach(f -> {
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));
151 context.put("sequenceAction", oldActionContext);