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;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
14 public abstract class AbstractActionContext implements ActionContext {
16 private static final Logger LOGGER = LoggerFactory.getLogger(AbstractActionContext.class);
18 public static final double TIME_TOLERANCE = 1e-6;
21 volatile boolean stopped;
22 ArrayList<Function1<Tuple0, Object>> scheduledNow = new ArrayList<>();
23 ArrayList<Function1<Tuple0, Object>> scheduledNextStep = new ArrayList<>();
24 ArrayList<Function1<StopReason, Object>> scheduledWhenStopped = new ArrayList<>();
25 PriorityQueue<Task> scheduledAt = new PriorityQueue<>();
27 public List<Exception> exceptions;
29 private static class Task implements Comparable<Task> {
31 final Function1<Tuple0, Object> continuation;
33 public Task(double time, Function1<Tuple0, Object> continuation) {
35 this.continuation = continuation;
39 public int compareTo(Task o) {
40 return Double.compare(time, o.time);
45 public double time() {
50 public void scheduleNow(Function1<Tuple0, Object> continuation) {
51 scheduledNow.add(continuation);
55 public void scheduleNextStep(Function1<Tuple0, Object> continuation) {
56 scheduledNextStep.add(continuation);
60 public void scheduleAt(double time, Function1<Tuple0, Object> continuation) {
61 if(time <= currentTime)
62 scheduleNow(continuation);
64 scheduledAt.add(new Task(time, continuation));
68 public void scheduleWhenStopped(Function1<StopReason, Object> continuation) {
69 scheduledWhenStopped.add(continuation);
74 stop(StopReason.STOPPED);
77 public void stop(StopReason reason) {
78 if (LOGGER.isTraceEnabled()) {
79 LOGGER.trace("stop context {}, reason={}", System.identityHashCode(this), reason, new Exception("trace"));
85 public boolean isStopped() {
91 public double handleStep(double currentTime) {
94 return Double.POSITIVE_INFINITY;
96 this.currentTime = currentTime;
98 ArrayList<Function1<Tuple0, Object>> temp = scheduledNow;
99 scheduledNow = scheduledNextStep;
100 scheduledNextStep = temp;
101 Collections.reverse(scheduledNow);
104 SCLContext context = SCLContext.getCurrent();
105 Object oldActionContext = context.put("sequenceAction", this);
108 while(!scheduledNow.isEmpty()) {
110 Function1<Tuple0, Object> currentContinuation = scheduledNow.remove(scheduledNow.size()-1);
111 currentContinuation.apply(Tuple0.INSTANCE);
112 currentContinuation = null;
113 } catch (Exception e) {
114 if (this.exceptions == null)
115 this.exceptions = new ArrayList<>();
116 this.exceptions.add(new RuntimeException("Action failure at " + currentTime + ": " + e.getMessage(), e));
119 Task firstTask = scheduledAt.peek();
120 if(firstTask == null) {
121 if (scheduledNextStep.isEmpty())
123 return Double.POSITIVE_INFINITY;
124 } else if(firstTask.time > currentTime+TIME_TOLERANCE) {
125 return firstTask.time;
127 scheduledAt.remove();
128 firstTask.continuation.apply(Tuple0.INSTANCE);
132 context.put("sequenceAction", oldActionContext);
137 private void handleStop(StopReason reason) {
138 synchronized (this) {
139 List<Function1<StopReason, Object>> stopFunctions = new ArrayList<>(scheduledWhenStopped);
140 scheduledWhenStopped.clear();
142 scheduledNextStep.clear();
145 SCLContext context = SCLContext.getCurrent();
146 Object oldActionContext = context.put("sequenceAction", this);
148 stopFunctions.forEach(f -> {
151 } catch (Exception e) {
152 if (this.exceptions == null)
153 this.exceptions = new ArrayList<>();
154 this.exceptions.add(new RuntimeException("Stop action failure at " + currentTime + ": " + e.getMessage(), e));
158 context.put("sequenceAction", oldActionContext);