diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/StreamlineSystem.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/StreamlineSystem.java
new file mode 100644
index 0000000000..ae5e1c456a
--- /dev/null
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/StreamlineSystem.java
@@ -0,0 +1,117 @@
+package gov.nasa.jpl.aerie.contrib.streamline;
+
+import gov.nasa.jpl.aerie.contrib.streamline.core.Resource;
+import gov.nasa.jpl.aerie.contrib.streamline.debugging.Logging;
+import gov.nasa.jpl.aerie.contrib.streamline.modeling.Registrar;
+import gov.nasa.jpl.aerie.contrib.streamline.modeling.Registration;
+import gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks.Clock;
+import gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks.ClockResources;
+import gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks.InstantClock;
+import gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks.InstantClockResources;
+import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
+
+import java.time.Instant;
+import java.util.Objects;
+
+import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.currentValue;
+
+public final class StreamlineSystem {
+ private static Resource CLOCK;
+ private static Resource ABSOLUTE_CLOCK;
+
+ private StreamlineSystem() {}
+
+ /**
+ * Arguments required for {@link StreamlineSystem#init}, packaged into an object for easier handling.
+ *
+ * Can be constructed directly, or through {@link InitArgs#builder}.
+ *
+ */
+ public record InitArgs(
+ gov.nasa.jpl.aerie.merlin.framework.Registrar baseRegistrar,
+ Registrar.ErrorBehavior errorBehavior,
+ Instant planStart) {
+ /**
+ * Returns a blank {@link Builder}.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Returns a {@link Builder} with some fields set to defaults generally appropriate for testing.
+ */
+ public static Builder testBuilder() {
+ return builder()
+ .errorBehavior(Registrar.ErrorBehavior.Throw)
+ .planStart(Instant.EPOCH);
+ }
+
+ public static class Builder {
+ private gov.nasa.jpl.aerie.merlin.framework.Registrar baseRegistrar;
+ private Registrar.ErrorBehavior errorBehavior;
+ private Instant planStart;
+
+ private Builder() {}
+
+ public Builder baseRegistrar(final gov.nasa.jpl.aerie.merlin.framework.Registrar baseRegistrar) {
+ this.baseRegistrar = baseRegistrar;
+ return this;
+ }
+
+ public Builder errorBehavior(final Registrar.ErrorBehavior errorBehavior) {
+ this.errorBehavior = errorBehavior;
+ return this;
+ }
+
+ public Builder planStart(final Instant planStart) {
+ this.planStart = planStart;
+ return this;
+ }
+
+ public InitArgs build() {
+ return new InitArgs(
+ Objects.requireNonNull(baseRegistrar, "baseRegistrar must be set"),
+ Objects.requireNonNull(errorBehavior, "errorBehavior must be set"),
+ Objects.requireNonNull(planStart, "planStart must be set")
+ );
+ }
+ }
+
+ }
+
+ /**
+ * Initialize all streamline singletons.
+ * This method should be called once as the first step of creating a model.
+ *
+ * This will call the following subordinate initialization methods:
+ *
+ * - {@link Logging#init}
+ * - {@link Registration#init}
+ *
+ * as well as initialize the singletons contained within this class.
+ *
+ */
+ public static void init(InitArgs args) {
+ CLOCK = ClockResources.clock();
+ ABSOLUTE_CLOCK = InstantClockResources.absoluteClock(args.planStart);
+ Logging.init(args.baseRegistrar);
+ Registration.init(args.baseRegistrar, args.errorBehavior);
+ }
+
+ public static Duration currentTime() {
+ return currentValue(CLOCK);
+ }
+
+ public static Resource simulationClock() {
+ return CLOCK;
+ }
+
+ public static Instant currentInstant() {
+ return currentValue(ABSOLUTE_CLOCK);
+ }
+
+ public static Resource absoluteClock() {
+ return ABSOLUTE_CLOCK;
+ }
+}
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/core/Resources.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/core/Resources.java
index 087815118a..16110ca182 100644
--- a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/core/Resources.java
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/core/Resources.java
@@ -1,8 +1,7 @@
package gov.nasa.jpl.aerie.contrib.streamline.core;
-import gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks.Clock;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
import gov.nasa.jpl.aerie.merlin.framework.Condition;
-import gov.nasa.jpl.aerie.merlin.framework.Scoped;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
import gov.nasa.jpl.aerie.merlin.protocol.types.Unit;
@@ -19,7 +18,6 @@
import static gov.nasa.jpl.aerie.contrib.streamline.core.Reactions.wheneverDynamicsChange;
import static gov.nasa.jpl.aerie.contrib.streamline.debugging.Dependencies.addDependency;
import static gov.nasa.jpl.aerie.contrib.streamline.debugging.Naming.*;
-import static gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks.Clock.clock;
import static gov.nasa.jpl.aerie.merlin.framework.ModelActions.*;
import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.ZERO;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.Discrete.discrete;
@@ -30,32 +28,6 @@
public final class Resources {
private Resources() {}
- /**
- * Ensure that Resources are initialized.
- *
- *
- * This method needs to be called during simulation initialization.
- * This method is idempotent; calling it multiple times is the same as calling it once.
- *
- */
- public static void init() {
- currentTime();
- }
-
- // TODO if Aerie provides either a `getElapsedTime` method or dynamic allocation of Cells, we can avoid this mutable static variable
- private static Resource CLOCK = resource(clock(ZERO));
- public static Duration currentTime() {
- try {
- return currentValue(CLOCK);
- } catch (Scoped.EmptyDynamicCellException | IllegalArgumentException e) {
- // If we're running unit tests, several simulations can happen without reloading the Resources class.
- // In that case, we'll have discarded the clock resource we were using, and get the above exception.
- // REVIEW: Is there a cleaner way to make sure this resource gets (re-)initialized?
- CLOCK = resource(clock(ZERO));
- return currentValue(CLOCK);
- }
- }
-
public static D currentData(Resource resource) {
return data(resource.getDynamics());
}
@@ -96,10 +68,10 @@ public static > V value(ErrorCatching> d
*/
public static > Condition dynamicsChange(Resource resource) {
final var startingDynamics = resource.getDynamics();
- final Duration startTime = currentTime();
+ final Duration startTime = StreamlineSystem.currentTime();
Condition result = (positive, atEarliest, atLatest) -> {
var currentDynamics = resource.getDynamics();
- var elapsedTime = currentTime().minus(startTime);
+ var elapsedTime = StreamlineSystem.currentTime().minus(startTime);
boolean haveChanged = startingDynamics.match(
start -> currentDynamics.match(
current -> !current.data().equals(start.data().step(elapsedTime)) ||
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/Logging.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/Logging.java
index f9741e8047..8d30374efb 100644
--- a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/Logging.java
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/Logging.java
@@ -14,14 +14,10 @@ private Logging() {}
/**
* Initialize the primary logger.
- * This is called when constructing a {@link gov.nasa.jpl.aerie.contrib.streamline.modeling.Registrar},
- * and does not need to be called directly by the model.
+ * This is called by {@link gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem#init}
+ * and should not be called by the model directly.
*/
public static void init(final Registrar registrar) {
- if (LOGGER == null) {
- LOGGER = new Logger(registrar);
- } else {
- LOGGER.warning("Attempting to re-initialize primary logger. This attempt is being ignored.");
- }
+ LOGGER = new Logger(registrar);
}
}
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/Tracing.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/Tracing.java
index 5a954aeaf1..0ec30328e7 100644
--- a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/Tracing.java
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/Tracing.java
@@ -1,5 +1,6 @@
package gov.nasa.jpl.aerie.contrib.streamline.debugging;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
import gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource;
import gov.nasa.jpl.aerie.contrib.streamline.core.Dynamics;
import gov.nasa.jpl.aerie.contrib.streamline.core.DynamicsEffect;
@@ -12,8 +13,6 @@
import java.util.Stack;
import java.util.function.Supplier;
-import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.currentTime;
-
/**
* Functions for debugging resources by tracing their calculation.
*/
@@ -90,9 +89,9 @@ public static Supplier trace(Supplier name, Supplier T traceAction(Supplier name, Supplier action) {
activeTracePoints.push(name.get());
- System.out.printf("TRACE: %s - %s start...%n", currentTime(), formatStack());
+ System.out.printf("TRACE: %s - %s start...%n", StreamlineSystem.currentTime(), formatStack());
T result = action.get();
- System.out.printf("TRACE: %s - %s: %s%n", currentTime(), formatStack(), result);
+ System.out.printf("TRACE: %s - %s: %s%n", StreamlineSystem.currentTime(), formatStack(), result);
activeTracePoints.pop();
return result;
}
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/Registrar.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/Registrar.java
index a8ab5ecce3..b2d7a3d9f2 100644
--- a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/Registrar.java
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/Registrar.java
@@ -55,8 +55,6 @@ public enum ErrorBehavior {
}
public Registrar(final gov.nasa.jpl.aerie.merlin.framework.Registrar baseRegistrar, final ErrorBehavior errorBehavior) {
- Resources.init();
- Logging.init(baseRegistrar);
this.baseRegistrar = baseRegistrar;
this.errorBehavior = errorBehavior;
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/Registration.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/Registration.java
new file mode 100644
index 0000000000..3ae7174abc
--- /dev/null
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/Registration.java
@@ -0,0 +1,18 @@
+package gov.nasa.jpl.aerie.contrib.streamline.modeling;
+
+public final class Registration {
+ private Registration() {}
+
+ public static Registrar REGISTRAR;
+
+ /**
+ * Initialize the primary registrar.
+ * This is called by {@link gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem#init}
+ * and should not be called by the model directly.
+ */
+ public static void init(
+ final gov.nasa.jpl.aerie.merlin.framework.Registrar baseRegistrar,
+ final Registrar.ErrorBehavior errorBehavior) {
+ REGISTRAR = new Registrar(baseRegistrar, errorBehavior);
+ }
+}
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/InstantClock.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/InstantClock.java
new file mode 100644
index 0000000000..e00bb5b323
--- /dev/null
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/InstantClock.java
@@ -0,0 +1,26 @@
+package gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks;
+
+import gov.nasa.jpl.aerie.contrib.streamline.core.Dynamics;
+import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+
+import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.addToInstant;
+
+/**
+ * A variation on {@link Clock} that represents an absolute {@link Instant}
+ * instead of a relative {@link Duration}.
+ */
+public record InstantClock(Instant extract) implements Dynamics {
+ @Override
+ public InstantClock step(Duration t) {
+ return new InstantClock(addToInstant(extract, t));
+ }
+
+ // TODO - this method belongs somewhere else...
+ // Making it package-private at least lets us move it later without dependency issues outside the library.
+ static Duration durationBetween(Instant start, Instant end) {
+ return Duration.of(ChronoUnit.MICROS.between(start, end), Duration.MICROSECONDS);
+ }
+}
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/InstantClockResources.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/InstantClockResources.java
new file mode 100644
index 0000000000..204ef2c3c2
--- /dev/null
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/InstantClockResources.java
@@ -0,0 +1,56 @@
+package gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks;
+
+import gov.nasa.jpl.aerie.contrib.streamline.core.*;
+import gov.nasa.jpl.aerie.contrib.streamline.core.monads.ResourceMonad;
+import gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.Discrete;
+import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
+
+import java.time.Instant;
+
+import static gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource.resource;
+import static gov.nasa.jpl.aerie.contrib.streamline.core.monads.ResourceMonad.map;
+import static gov.nasa.jpl.aerie.contrib.streamline.debugging.Naming.name;
+import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteResources.constant;
+
+public class InstantClockResources {
+ /**
+ * Create an absolute clock that starts now at the given start time.
+ */
+ public static MutableResource absoluteClock(Instant startTime) {
+ return resource(new InstantClock(startTime));
+ }
+
+ public static Resource addToInstant(Instant zeroTime, Resource relativeClock) {
+ return addToInstant(constant(zeroTime), relativeClock);
+ }
+
+ public static Resource addToInstant(Resource> zeroTime, Resource relativeClock) {
+ return name(
+ map(zeroTime, relativeClock, (zero, clock) ->
+ new InstantClock(Duration.addToInstant(zero.extract(), clock.extract()))),
+ "%s + %s",
+ zeroTime,
+ relativeClock);
+ }
+
+ public static Resource relativeTo(Resource clock, Resource> zeroTime) {
+ return name(ResourceMonad.map(clock, zeroTime, (c, t) -> new Clock(InstantClock.durationBetween(t.extract(), c.extract()))),
+ "%s relative to %s", clock, zeroTime);
+ }
+
+ public static Resource> lessThan(Resource clock, Resource> threshold) {
+ return ClockResources.lessThan(relativeTo(clock, threshold), constant(Duration.ZERO));
+ }
+
+ public static Resource> lessThanOrEquals(Resource clock, Resource> threshold) {
+ return ClockResources.lessThanOrEquals(relativeTo(clock, threshold), constant(Duration.ZERO));
+ }
+
+ public static Resource> greaterThan(Resource clock, Resource> threshold) {
+ return ClockResources.greaterThan(relativeTo(clock, threshold), constant(Duration.ZERO));
+ }
+
+ public static Resource> greaterThanOrEquals(Resource clock, Resource> threshold) {
+ return ClockResources.greaterThanOrEquals(relativeTo(clock, threshold), constant(Duration.ZERO));
+ }
+}
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/VariableInstantClock.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/VariableInstantClock.java
new file mode 100644
index 0000000000..7915396af3
--- /dev/null
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/VariableInstantClock.java
@@ -0,0 +1,19 @@
+package gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks;
+
+import gov.nasa.jpl.aerie.contrib.streamline.core.Dynamics;
+import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
+
+import java.time.Instant;
+
+import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.addToInstant;
+
+/**
+ * A variation on {@link VariableClock} that represents an absolute {@link Instant}
+ * instead of a relative {@link Duration}.
+ */
+public record VariableInstantClock(Instant extract, int multiplier) implements Dynamics {
+ @Override
+ public VariableInstantClock step(Duration t) {
+ return new VariableInstantClock(addToInstant(extract, t.times(multiplier)), multiplier);
+ }
+}
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/VariableInstantClockEffects.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/VariableInstantClockEffects.java
new file mode 100644
index 0000000000..019a31ee4c
--- /dev/null
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/VariableInstantClockEffects.java
@@ -0,0 +1,33 @@
+package gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks;
+
+import gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource;
+
+import java.time.Instant;
+
+import static gov.nasa.jpl.aerie.contrib.streamline.core.monads.DynamicsMonad.effect;
+import static gov.nasa.jpl.aerie.contrib.streamline.debugging.Naming.name;
+
+public final class VariableInstantClockEffects {
+ private VariableInstantClockEffects() {}
+
+ /**
+ * Stop the clock without affecting the current time.
+ */
+ public static void pause(MutableResource clock) {
+ clock.emit("Pause", effect($ -> new VariableInstantClock($.extract(), 0)));
+ }
+
+ /**
+ * Start the clock without affecting the current time.
+ */
+ public static void start(MutableResource clock) {
+ clock.emit("Start", effect($ -> new VariableInstantClock($.extract(), 1)));
+ }
+
+ /**
+ * Reset the clock to the given time, without affecting how fast it's running.
+ */
+ public static void reset(MutableResource clock, Instant newTime) {
+ clock.emit(name(effect($ -> new VariableInstantClock(newTime, $.multiplier())), "Reset to %s", newTime));
+ }
+}
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/VariableInstantClockResources.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/VariableInstantClockResources.java
new file mode 100644
index 0000000000..83df2de990
--- /dev/null
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/VariableInstantClockResources.java
@@ -0,0 +1,42 @@
+package gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks;
+
+import gov.nasa.jpl.aerie.contrib.streamline.core.Resource;
+import gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.Discrete;
+import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
+
+import java.time.Instant;
+
+import static gov.nasa.jpl.aerie.contrib.streamline.core.monads.ResourceMonad.map;
+import static gov.nasa.jpl.aerie.contrib.streamline.debugging.Naming.name;
+import static gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks.InstantClock.durationBetween;
+import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteResources.constant;
+
+public final class VariableInstantClockResources {
+ private VariableInstantClockResources() {}
+
+ public static Resource relativeTo(Resource clock, Resource> zeroTime) {
+ return name(map(clock, zeroTime, (c, t) ->
+ new VariableClock(durationBetween(c.extract(), t.extract()), c.multiplier())),
+ "%s relative to %s", clock, zeroTime);
+ }
+
+ public static Resource> lessThan(Resource clock, Resource> threshold) {
+ return VariableClockResources.lessThan(relativeTo(clock, threshold), constant(Duration.ZERO));
+ }
+
+ public static Resource> lessThanOrEquals(Resource clock, Resource> threshold) {
+ return VariableClockResources.lessThanOrEquals(relativeTo(clock, threshold), constant(Duration.ZERO));
+ }
+
+ public static Resource> greaterThan(Resource clock, Resource> threshold) {
+ return VariableClockResources.greaterThan(relativeTo(clock, threshold), constant(Duration.ZERO));
+ }
+
+ public static Resource> greaterThanOrEquals(Resource clock, Resource> threshold) {
+ return VariableClockResources.greaterThanOrEquals(relativeTo(clock, threshold), constant(Duration.ZERO));
+ }
+
+ public static Resource between(Resource start, Resource end) {
+ return map(start, end, (s, e) -> new VariableClock(durationBetween(s.extract(), e.extract()), e.multiplier() - s.multiplier()));
+ }
+}
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/DiscreteResources.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/DiscreteResources.java
index 5a54a9a229..80c7816e54 100644
--- a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/DiscreteResources.java
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/DiscreteResources.java
@@ -19,6 +19,8 @@
import java.util.function.BiPredicate;
import java.util.function.Supplier;
+import static gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem.currentInstant;
+import static gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem.simulationClock;
import static gov.nasa.jpl.aerie.contrib.streamline.core.CellRefV2.autoEffects;
import static gov.nasa.jpl.aerie.contrib.streamline.core.CellRefV2.testing;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Expiring.expiring;
@@ -121,10 +123,9 @@ public static > Resource> sampled
* Resource value is the value associated with the greatest key in segments not exceeding
* the current simulation time, or valueBeforeFirstEntry if every key exceeds current simulation time.
*/
- public static Resource> precomputed(
+ public static Resource> precomputed$(
final V valueBeforeFirstEntry, final NavigableMap segments) {
- var clock = clock();
- return signalling(bind(clock, (Clock clock$) -> {
+ return signalling(bind(simulationClock(), (Clock clock$) -> {
var t = clock$.extract();
var entry = segments.floorEntry(t);
var value = entry == null ? valueBeforeFirstEntry : entry.getValue();
@@ -139,14 +140,15 @@ public static Resource> precomputed(
* the current simulation time, or valueBeforeFirstEntry if every key exceeds current simulation time.
*/
public static Resource> precomputed(
- final V valueBeforeFirstEntry, final NavigableMap segments, final Instant simulationStartTime) {
+ final V valueBeforeFirstEntry, final NavigableMap segments) {
+ var simulationStartTime = currentInstant();
var segmentsUsingDurationKeys = new TreeMap();
for (var entry : segments.entrySet()) {
segmentsUsingDurationKeys.put(
Duration.of(ChronoUnit.MICROS.between(simulationStartTime, entry.getKey()), Duration.MICROSECONDS),
entry.getValue());
}
- return precomputed(valueBeforeFirstEntry, segmentsUsingDurationKeys);
+ return precomputed$(valueBeforeFirstEntry, segmentsUsingDurationKeys);
}
/**
diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/PolynomialEffects.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/PolynomialEffects.java
index 366567ea90..51f5b539b6 100644
--- a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/PolynomialEffects.java
+++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/PolynomialEffects.java
@@ -1,6 +1,6 @@
package gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial;
-import gov.nasa.jpl.aerie.contrib.streamline.core.Resources;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
import gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource;
import gov.nasa.jpl.aerie.contrib.streamline.unit_aware.StandardUnits;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
@@ -94,9 +94,9 @@ public static void restore(MutableResource resource, double rate, Du
private static void withConsumableEffects(String verb, MutableResource resource, Polynomial profile, Runnable action) {
resource.emit(name(effect($ -> $.subtract(profile)),
"Start %s according to profile %s", verb, profile));
- final Duration start = Resources.currentTime();
+ final Duration start = StreamlineSystem.currentTime();
action.run();
- final Duration elapsedTime = Resources.currentTime().minus(start);
+ final Duration elapsedTime = StreamlineSystem.currentTime().minus(start);
// Nullify ongoing effects by adding a profile with the same behavior,
// but with an initial value of 0
final Polynomial steppedProfile = profile.step(elapsedTime);
@@ -158,9 +158,9 @@ public static void providing(MutableResource resource, double amount
private static void withNonConsumableEffect(String verb, MutableResource resource, Polynomial profile, Runnable action) {
resource.emit(name(effect($ -> $.subtract(profile)),
"Start %s profile %s", verb, profile));
- final Duration start = Resources.currentTime();
+ final Duration start = StreamlineSystem.currentTime();
action.run();
- final Duration elapsedTime = Resources.currentTime().minus(start);
+ final Duration elapsedTime = StreamlineSystem.currentTime().minus(start);
// Reset by adding a counteracting profile
final Polynomial counteractingProfile = profile.step(elapsedTime);
resource.emit(name(effect($ -> $.add(counteractingProfile)),
diff --git a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/core/CellRefV2Test.java b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/core/CellRefV2Test.java
index c77bb601c8..d6bd2d4a22 100644
--- a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/core/CellRefV2Test.java
+++ b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/core/CellRefV2Test.java
@@ -1,5 +1,7 @@
package gov.nasa.jpl.aerie.contrib.streamline.core;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem.InitArgs;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.Discrete;
import gov.nasa.jpl.aerie.merlin.framework.Registrar;
import gov.nasa.jpl.aerie.merlin.framework.junit.MerlinExtension;
@@ -27,10 +29,12 @@ class MutableResourceTest {
@TestInstance(Lifecycle.PER_CLASS)
class NonCommutingEffects {
public NonCommutingEffects(final Registrar registrar) {
- Resources.init();
+ StreamlineSystem.init(InitArgs.testBuilder().baseRegistrar(registrar).build());
+
+ cell = MutableResource.resource(discrete(42), noncommutingEffects());
}
- private final MutableResource> cell = MutableResource.resource(discrete(42), noncommutingEffects());
+ private final MutableResource> cell;
@Test
void gets_initial_value_if_no_effects_are_emitted() {
@@ -66,10 +70,12 @@ void throws_exception_when_concurrent_effects_are_applied() {
@TestInstance(Lifecycle.PER_CLASS)
class CommutingEffects {
public CommutingEffects(final Registrar registrar) {
- Resources.init();
+ StreamlineSystem.init(InitArgs.testBuilder().baseRegistrar(registrar).build());
+
+ cell = MutableResource.resource(discrete(42), commutingEffects());
}
- private final MutableResource> cell = MutableResource.resource(discrete(42), commutingEffects());
+ private final MutableResource> cell;
@Test
void gets_initial_value_if_no_effects_are_emitted() {
@@ -109,10 +115,12 @@ void applies_concurrent_effects_in_any_order() {
@TestInstance(Lifecycle.PER_CLASS)
class AutoEffects {
public AutoEffects(final Registrar registrar) {
- Resources.init();
+ StreamlineSystem.init(InitArgs.testBuilder().baseRegistrar(registrar).build());
+
+ cell = MutableResource.resource(discrete(42), autoEffects());
}
- private final MutableResource> cell = MutableResource.resource(discrete(42), autoEffects());
+ private final MutableResource> cell;
@Test
void gets_initial_value_if_no_effects_are_emitted() {
diff --git a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/DependenciesTest.java b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/DependenciesTest.java
index 8014216f63..69e66d1483 100644
--- a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/DependenciesTest.java
+++ b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/debugging/DependenciesTest.java
@@ -1,16 +1,20 @@
package gov.nasa.jpl.aerie.contrib.streamline.debugging;
-import gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resource;
+import gov.nasa.jpl.aerie.contrib.streamline.core.Resources;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.Discrete;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteResources;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.Polynomial;
+import gov.nasa.jpl.aerie.merlin.framework.Registrar;
import gov.nasa.jpl.aerie.merlin.framework.junit.MerlinExtension;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
+import java.time.Instant;
+
import static gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource.resource;
import static gov.nasa.jpl.aerie.contrib.streamline.core.monads.ResourceMonad.*;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.Polynomial.polynomial;
@@ -21,12 +25,22 @@
@ExtendWith(MerlinExtension.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DependenciesTest {
- Resource> constantTrue = DiscreteResources.constant(true);
- Resource constant1234 = constant(1234);
- Resource constant5678 = constant(5678);
- Resource polynomialCell = resource(polynomial(1));
- Resource derived = map(constantTrue, constant1234, constant5678,
- (b, x, y) -> b.extract() ? x : y);
+ public DependenciesTest(final Registrar registrar) {
+ StreamlineSystem.init(StreamlineSystem.InitArgs.testBuilder().baseRegistrar(registrar).build());
+
+ constantTrue = DiscreteResources.constant(true);
+ constant1234 = constant(1234);
+ constant5678 = constant(5678);
+ polynomialCell = resource(polynomial(1));
+ derived = map(constantTrue, constant1234, constant5678,
+ (b, x, y) -> b.extract() ? x : y);
+ }
+
+ Resource> constantTrue;
+ Resource constant1234;
+ Resource constant5678;
+ Resource polynomialCell;
+ Resource derived;
@Test
void constants_are_named_by_their_value() {
diff --git a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/SimulationClockTest.java b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/SimulationClockTest.java
new file mode 100644
index 0000000000..c8b77a2ede
--- /dev/null
+++ b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/clocks/SimulationClockTest.java
@@ -0,0 +1,71 @@
+package gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks;
+
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
+import gov.nasa.jpl.aerie.merlin.framework.Registrar;
+import gov.nasa.jpl.aerie.merlin.framework.junit.MerlinExtension;
+import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+
+import static gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem.*;
+import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.currentValue;
+import static gov.nasa.jpl.aerie.merlin.framework.ModelActions.delay;
+import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.HOUR;
+import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.ZERO;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@ExtendWith(MerlinExtension.class)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class SimulationClockTest {
+ // This time is unlikely to be a hard-coded default anywhere
+ public static final Instant PLAN_START = Instant.parse("2020-01-02T03:04:05Z");
+
+ public SimulationClockTest(final Registrar registrar) {
+ StreamlineSystem.init(InitArgs.testBuilder()
+ .baseRegistrar(registrar)
+ .planStart(PLAN_START)
+ .build());
+ }
+
+ @Test
+ public void relative_clock_starts_at_zero() {
+ assertEquals(ZERO, currentTime());
+ assertEquals(ZERO, currentValue(simulationClock()));
+ }
+
+ @Test
+ public void absolute_clock_starts_at_plan_start() {
+ assertEquals(PLAN_START, currentInstant());
+ assertEquals(PLAN_START, currentValue(absoluteClock()));
+ }
+
+ @Test
+ public void relative_clock_advances_at_unit_rate() {
+ delay(1, HOUR);
+ assertEquals(Duration.of(1, HOUR), currentTime());
+ assertEquals(Duration.of(1, HOUR), currentValue(simulationClock()));
+ delay(1, HOUR);
+ assertEquals(Duration.of(2, HOUR), currentTime());
+ assertEquals(Duration.of(2, HOUR), currentValue(simulationClock()));
+ delay(1, HOUR);
+ assertEquals(Duration.of(3, HOUR), currentTime());
+ assertEquals(Duration.of(3, HOUR), currentValue(simulationClock()));
+ }
+
+ @Test
+ public void absolute_clock_advances_at_unit_rate() {
+ delay(1, HOUR);
+ assertEquals(PLAN_START.plus(1, ChronoUnit.HOURS), currentInstant());
+ assertEquals(PLAN_START.plus(1, ChronoUnit.HOURS), currentValue(absoluteClock()));
+ delay(1, HOUR);
+ assertEquals(PLAN_START.plus(2, ChronoUnit.HOURS), currentInstant());
+ assertEquals(PLAN_START.plus(2, ChronoUnit.HOURS), currentValue(absoluteClock()));
+ delay(1, HOUR);
+ assertEquals(PLAN_START.plus(3, ChronoUnit.HOURS), currentInstant());
+ assertEquals(PLAN_START.plus(3, ChronoUnit.HOURS), currentValue(absoluteClock()));
+ }
+}
diff --git a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/DiscreteEffectsTest.java b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/DiscreteEffectsTest.java
index 49d4a953e8..245cd3a896 100644
--- a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/DiscreteEffectsTest.java
+++ b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/DiscreteEffectsTest.java
@@ -1,5 +1,6 @@
package gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
import gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource;
import gov.nasa.jpl.aerie.contrib.streamline.core.ErrorCatching;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resources;
@@ -14,7 +15,6 @@
import org.junit.jupiter.api.extension.ExtendWith;
import static gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource.resource;
-import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.currentTime;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.currentValue;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.Discrete.discrete;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteEffects.*;
@@ -41,7 +41,7 @@
@TestInstance(Lifecycle.PER_CLASS)
class DiscreteEffectsTest {
public DiscreteEffectsTest(final Registrar registrar) {
- Resources.init();
+ StreamlineSystem.init(StreamlineSystem.InitArgs.testBuilder().baseRegistrar(registrar).build());
}
private final MutableResource> settable = resource(discrete(42));
@@ -162,12 +162,12 @@ void using_decreases_value_while_action_is_running() {
@Test
void using_runs_synchronously() {
- Duration start = currentTime();
+ Duration start = StreamlineSystem.currentTime();
using(nonconsumable, 3.14, () -> {
- assertEquals(start, currentTime());
+ assertEquals(start, StreamlineSystem.currentTime());
delay(MINUTE);
});
- assertEquals(start.plus(MINUTE), currentTime());
+ assertEquals(start.plus(MINUTE), StreamlineSystem.currentTime());
}
@Test
diff --git a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/PrecomputedTest.java b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/PrecomputedTest.java
index 3849203bf5..497b0d3be9 100644
--- a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/PrecomputedTest.java
+++ b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/discrete/PrecomputedTest.java
@@ -1,5 +1,6 @@
package gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resource;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resources;
import gov.nasa.jpl.aerie.merlin.framework.Registrar;
@@ -16,6 +17,7 @@
import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.currentValue;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteResources.precomputed;
+import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteResources.precomputed$;
import static gov.nasa.jpl.aerie.merlin.framework.ModelActions.delay;
import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.*;
import static org.junit.jupiter.api.Assertions.*;
@@ -27,11 +29,34 @@
@TestInstance(Lifecycle.PER_CLASS)
public class PrecomputedTest {
public PrecomputedTest(final Registrar registrar) {
- Resources.init();
+ StreamlineSystem.init(StreamlineSystem.InitArgs.testBuilder()
+ .baseRegistrar(registrar)
+ .planStart(Instant.parse("2023-10-18T00:00:00Z"))
+ .build());
+ precomputedAsAConstant = precomputed$(4, new TreeMap<>());
+ precomputedWithOneTransitionInFuture = precomputed$(0, new TreeMap<>(Map.of(MINUTE, 10)));
+ precomputedWithOneTransitionInPast = precomputed$(0, new TreeMap<>(Map.of(duration(-1, MINUTE), 10)));
+ precomputedWithMultipleTransitionsInFuture = precomputed$(0, new TreeMap<>(Map.of(
+ duration(2, MINUTE), 5,
+ duration(5, MINUTE), 10,
+ duration(6, MINUTE), 15)));
+ precomputedWithMultipleTransitionsInPast = precomputed$(0, new TreeMap<>(Map.of(
+ duration(-2, MINUTE), 5,
+ duration(-5, MINUTE), 10,
+ duration(-6, MINUTE), 15)));
+ precomputedWithTransitionsInPastAndFuture = precomputed$(0, new TreeMap<>(Map.of(
+ duration(-5, MINUTE), 25,
+ duration(-2, MINUTE), 5,
+ duration(5, MINUTE), 10,
+ duration(6, MINUTE), 15)));
+ precomputedWithInstantKeys = precomputed(0, new TreeMap<>(Map.of(
+ Instant.parse("2023-10-17T23:55:00Z"), 25,
+ Instant.parse("2023-10-17T23:58:00Z"), 5,
+ Instant.parse("2023-10-18T00:05:00Z"), 10,
+ Instant.parse("2023-10-18T00:06:00Z"), 15)));
}
- final Resource> precomputedAsAConstant =
- precomputed(4, new TreeMap<>());
+ final Resource> precomputedAsAConstant;
@Test
void precomputed_with_no_transitions_uses_default_value_forever() {
assertEquals(4, currentValue(precomputedAsAConstant));
@@ -41,8 +66,7 @@ void precomputed_with_no_transitions_uses_default_value_forever() {
assertEquals(4, currentValue(precomputedAsAConstant));
}
- final Resource> precomputedWithOneTransitionInFuture =
- precomputed(0, new TreeMap<>(Map.of(MINUTE, 10)));
+ final Resource> precomputedWithOneTransitionInFuture;
@Test
void precomputed_with_transition_in_future_changes_at_that_time() {
assertEquals(0, currentValue(precomputedWithOneTransitionInFuture));
@@ -53,8 +77,7 @@ void precomputed_with_transition_in_future_changes_at_that_time() {
assertEquals(10, currentValue(precomputedWithOneTransitionInFuture));
}
- final Resource> precomputedWithOneTransitionInPast =
- precomputed(0, new TreeMap<>(Map.of(duration(-1, MINUTE), 10)));
+ final Resource> precomputedWithOneTransitionInPast;
@Test
void precomputed_with_transition_in_past_uses_that_value_forever() {
assertEquals(10, currentValue(precomputedWithOneTransitionInPast));
@@ -64,11 +87,7 @@ void precomputed_with_transition_in_past_uses_that_value_forever() {
assertEquals(10, currentValue(precomputedWithOneTransitionInPast));
}
- final Resource> precomputedWithMultipleTransitionsInFuture =
- precomputed(0, new TreeMap<>(Map.of(
- duration(2, MINUTE), 5,
- duration(5, MINUTE), 10,
- duration(6, MINUTE), 15)));
+ final Resource> precomputedWithMultipleTransitionsInFuture;
@Test
void precomputed_with_multiple_transitions_in_future_goes_through_each_in_turn() {
assertEquals(0, currentValue(precomputedWithMultipleTransitionsInFuture));
@@ -81,11 +100,7 @@ void precomputed_with_multiple_transitions_in_future_goes_through_each_in_turn()
assertEquals(15, currentValue(precomputedWithMultipleTransitionsInFuture));
}
- final Resource> precomputedWithMultipleTransitionsInPast =
- precomputed(0, new TreeMap<>(Map.of(
- duration(-2, MINUTE), 5,
- duration(-5, MINUTE), 10,
- duration(-6, MINUTE), 15)));
+ final Resource> precomputedWithMultipleTransitionsInPast;
@Test
void precomputed_with_multiple_transition_in_past_uses_last_value_forever() {
assertEquals(5, currentValue(precomputedWithMultipleTransitionsInPast));
@@ -95,12 +110,7 @@ void precomputed_with_multiple_transition_in_past_uses_last_value_forever() {
assertEquals(5, currentValue(precomputedWithMultipleTransitionsInPast));
}
- final Resource> precomputedWithTransitionsInPastAndFuture =
- precomputed(0, new TreeMap<>(Map.of(
- duration(-5, MINUTE), 25,
- duration(-2, MINUTE), 5,
- duration(5, MINUTE), 10,
- duration(6, MINUTE), 15)));
+ final Resource> precomputedWithTransitionsInPastAndFuture;
@Test
void precomputed_with_transitions_in_past_and_future_chooses_starting_value_and_changes_later() {
assertEquals(5, currentValue(precomputedWithTransitionsInPastAndFuture));
@@ -112,13 +122,7 @@ void precomputed_with_transitions_in_past_and_future_chooses_starting_value_and_
assertEquals(15, currentValue(precomputedWithTransitionsInPastAndFuture));
}
- final Resource> precomputedWithInstantKeys =
- precomputed(0, new TreeMap<>(Map.of(
- Instant.parse("2023-10-17T23:55:00Z"), 25,
- Instant.parse("2023-10-17T23:58:00Z"), 5,
- Instant.parse("2023-10-18T00:05:00Z"), 10,
- Instant.parse("2023-10-18T00:06:00Z"), 15)),
- Instant.parse("2023-10-18T00:00:00Z"));
+ final Resource> precomputedWithInstantKeys;
@Test
void precomputed_with_instant_keys_behaves_identically_to_equivalent_duration_offsets() {
assertEquals(5, currentValue(precomputedWithInstantKeys));
diff --git a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/ComparisonsTest.java b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/ComparisonsTest.java
index a7cad57998..c0ce64b096 100644
--- a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/ComparisonsTest.java
+++ b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/ComparisonsTest.java
@@ -1,5 +1,6 @@
package gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
import gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource;
import gov.nasa.jpl.aerie.contrib.streamline.core.Expiry;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resource;
@@ -29,21 +30,23 @@
@TestInstance(Lifecycle.PER_CLASS)
public class ComparisonsTest {
public ComparisonsTest(final Registrar registrar) {
- Resources.init();
+ StreamlineSystem.init(StreamlineSystem.InitArgs.testBuilder().baseRegistrar(registrar).build());
+
+ p = resource(polynomial(0));
+ q = resource(polynomial(0));
+ p_lt_q = lessThan(p, q);
+ p_lte_q = lessThanOrEquals(p, q);
+ p_gt_q = greaterThan(p, q);
+ p_gte_q = greaterThanOrEquals(p, q);
+ min_p_q = min(p, q);
+ min_q_p = min(q, p);
+ max_p_q = max(p, q);
+ max_q_p = max(q, p);
}
- private final MutableResource p = resource(polynomial(0));
- private final MutableResource q = resource(polynomial(0));
-
- private final Resource> p_lt_q = lessThan(p, q);
- private final Resource> p_lte_q = lessThanOrEquals(p, q);
- private final Resource> p_gt_q = greaterThan(p, q);
- private final Resource> p_gte_q = greaterThanOrEquals(p, q);
-
- private final Resource min_p_q = min(p, q);
- private final Resource min_q_p = min(q, p);
- private final Resource max_p_q = max(p, q);
- private final Resource max_q_p = max(q, p);
+ private final MutableResource p, q;
+ private final Resource> p_lt_q, p_lte_q, p_gt_q, p_gte_q;
+ private final Resource min_p_q, min_q_p, max_p_q, max_q_p;
@Test
void comparing_distinct_constants() {
diff --git a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/LinearBoundaryConsistencySolverTest.java b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/LinearBoundaryConsistencySolverTest.java
index eceec5b70e..612586ccc1 100644
--- a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/LinearBoundaryConsistencySolverTest.java
+++ b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/LinearBoundaryConsistencySolverTest.java
@@ -1,7 +1,9 @@
package gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
import gov.nasa.jpl.aerie.contrib.streamline.core.*;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.LinearBoundaryConsistencySolver.Domain;
+import gov.nasa.jpl.aerie.merlin.framework.Registrar;
import gov.nasa.jpl.aerie.merlin.framework.junit.MerlinExtension;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
import org.junit.jupiter.api.Nested;
@@ -25,11 +27,13 @@ class LinearBoundaryConsistencySolverTest {
@ExtendWith(MerlinExtension.class)
@TestInstance(Lifecycle.PER_CLASS)
class SingleVariableSingleConstraint {
- MutableResource driver = resource(polynomial(10));
+ MutableResource driver;
Resource result;
- public SingleVariableSingleConstraint() {
- Resources.init();
+ public SingleVariableSingleConstraint(final Registrar registrar) {
+ StreamlineSystem.init(StreamlineSystem.InitArgs.testBuilder().baseRegistrar(registrar).build());
+
+ driver = resource(polynomial(10));
var solver = new LinearBoundaryConsistencySolver("SingleVariableSingleConstraint");
var v = solver.variable("v", Domain::upperBound);
@@ -65,13 +69,15 @@ void results_evolve_with_time() {
@ExtendWith(MerlinExtension.class)
@TestInstance(Lifecycle.PER_CLASS)
class SingleVariableMultipleConstraint {
- MutableResource lowerBound1 = resource(polynomial(10));
- MutableResource lowerBound2 = resource(polynomial(20));
- MutableResource upperBound = resource(polynomial(30));
+ MutableResource lowerBound1, lowerBound2, upperBound;
Resource result;
- public SingleVariableMultipleConstraint() {
- Resources.init();
+ public SingleVariableMultipleConstraint(final Registrar registrar) {
+ StreamlineSystem.init(StreamlineSystem.InitArgs.testBuilder().baseRegistrar(registrar).build());
+
+ lowerBound1 = resource(polynomial(10));
+ lowerBound2 = resource(polynomial(20));
+ upperBound = resource(polynomial(30));
var solver = new LinearBoundaryConsistencySolver("SingleVariableMultipleConstraint");
var v = solver.variable("v", Domain::lowerBound);
@@ -141,11 +147,13 @@ void failures_are_cleared_if_problem_becomes_feasible_again() {
@ExtendWith(MerlinExtension.class)
@TestInstance(Lifecycle.PER_CLASS)
class ScalingConstraint {
- MutableResource driver = resource(polynomial(10));
+ MutableResource driver;
Resource result;
- public ScalingConstraint() {
- Resources.init();
+ public ScalingConstraint(final Registrar registrar) {
+ StreamlineSystem.init(StreamlineSystem.InitArgs.testBuilder().baseRegistrar(registrar).build());
+
+ driver = resource(polynomial(10));
var solver = new LinearBoundaryConsistencySolver("ScalingConstraint");
var v = solver.variable("v", Domain::upperBound);
@@ -171,12 +179,14 @@ void scaling_is_respected_for_later_solutions() {
@ExtendWith(MerlinExtension.class)
@TestInstance(Lifecycle.PER_CLASS)
class MultipleVariables {
- MutableResource upperBound = resource(polynomial(10));
- MutableResource upperBoundOnC = resource(polynomial(5));
+ MutableResource upperBound, upperBoundOnC;
Resource a, b, c;
- public MultipleVariables() {
- Resources.init();
+ public MultipleVariables(final Registrar registrar) {
+ StreamlineSystem.init(StreamlineSystem.InitArgs.testBuilder().baseRegistrar(registrar).build());
+
+ upperBound = resource(polynomial(10));
+ upperBoundOnC = resource(polynomial(5));
var solver = new LinearBoundaryConsistencySolver("MultipleVariablesSingleConstraint");
var a = solver.variable("a", Domain::upperBound);
diff --git a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/PrecomputedTest.java b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/PrecomputedTest.java
index 47427baf17..849f40a84d 100644
--- a/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/PrecomputedTest.java
+++ b/contrib/src/test/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/PrecomputedTest.java
@@ -1,5 +1,6 @@
package gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resource;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resources;
import gov.nasa.jpl.aerie.merlin.framework.Registrar;
@@ -25,11 +26,27 @@
@TestInstance(Lifecycle.PER_CLASS)
public class PrecomputedTest {
public PrecomputedTest(final Registrar registrar) {
- Resources.init();
+ StreamlineSystem.init(StreamlineSystem.InitArgs.testBuilder().baseRegistrar(registrar).build());
+
+ precomputedAsConstantInPast = precomputed(new TreeMap<>(Map.of(duration(-1, MINUTE), 4.0)));
+ precomputedAsConstantInFuture = precomputed(new TreeMap<>(Map.of(duration(2, HOUR), 4.0)));
+ precomputedWithSingleInteriorSegmentInPast = precomputed(new TreeMap<>(Map.of(
+ duration(-100, SECOND), 0.0,
+ duration(-50, SECOND), 5.0)));
+ precomputedWithSingleInteriorSegmentInFuture = precomputed(new TreeMap<>(Map.of(
+ duration(50, SECOND), 0.0,
+ duration(100, SECOND), 5.0)));
+ precomputedStartingInInterior = precomputed(new TreeMap<>(Map.of(
+ duration(-50, SECOND), 0.0,
+ duration(50, SECOND), 10.0)));
+ precomputedWithMultipleSegments = precomputed(new TreeMap<>(Map.of(
+ duration(-50, SECOND), 0.0,
+ duration(50, SECOND), 10.0,
+ duration(60, SECOND), 30.0,
+ duration(90, SECOND), -30.0)));
}
- final Resource precomputedAsConstantInPast =
- precomputed(new TreeMap<>(Map.of(duration(-1, MINUTE), 4.0)));
+ final Resource precomputedAsConstantInPast;
@Test
void precomputed_with_single_point_in_past_extrapolates_that_value_forever() {
assertValueEquals(4.0, precomputedAsConstantInPast);
@@ -39,8 +56,7 @@ void precomputed_with_single_point_in_past_extrapolates_that_value_forever() {
assertValueEquals(4.0, precomputedAsConstantInPast);
}
- final Resource precomputedAsConstantInFuture =
- precomputed(new TreeMap<>(Map.of(duration(2, HOUR), 4.0)));
+ final Resource precomputedAsConstantInFuture;
@Test
void precomputed_with_single_point_in_future_extrapolates_that_value_forever() {
assertValueEquals(4.0, precomputedAsConstantInFuture);
@@ -54,10 +70,7 @@ void precomputed_with_single_point_in_future_extrapolates_that_value_forever() {
assertValueEquals(4.0, precomputedAsConstantInFuture);
}
- final Resource precomputedWithSingleInteriorSegmentInPast =
- precomputed(new TreeMap<>(Map.of(
- duration(-100, SECOND), 0.0,
- duration(-50, SECOND), 5.0)));
+ final Resource precomputedWithSingleInteriorSegmentInPast;
@Test
void precomputed_with_single_interior_segment_in_past_extrapolates_final_value() {
assertValueEquals(5.0, precomputedWithSingleInteriorSegmentInPast);
@@ -67,10 +80,7 @@ void precomputed_with_single_interior_segment_in_past_extrapolates_final_value()
assertValueEquals(5.0, precomputedWithSingleInteriorSegmentInPast);
}
- final Resource precomputedWithSingleInteriorSegmentInFuture =
- precomputed(new TreeMap<>(Map.of(
- duration(50, SECOND), 0.0,
- duration(100, SECOND), 5.0)));
+ final Resource precomputedWithSingleInteriorSegmentInFuture;
@Test
void precomputed_with_single_interior_segment_in_future_interpolates_that_segment() {
assertValueEquals(0.0, precomputedWithSingleInteriorSegmentInFuture);
@@ -90,10 +100,7 @@ void precomputed_with_single_interior_segment_in_future_interpolates_that_segmen
assertValueEquals(5.0, precomputedWithSingleInteriorSegmentInFuture);
}
- final Resource precomputedStartingInInterior =
- precomputed(new TreeMap<>(Map.of(
- duration(-50, SECOND), 0.0,
- duration(50, SECOND), 10.0)));
+ final Resource precomputedStartingInInterior;
void precomputed_starting_in_interior_interpolates_over_full_segment() {
assertValueEquals(5.0, precomputedStartingInInterior);
delay(10, SECOND);
@@ -110,12 +117,7 @@ void precomputed_starting_in_interior_interpolates_over_full_segment() {
assertValueEquals(10.0, precomputedStartingInInterior);
}
- final Resource precomputedWithMultipleSegments =
- precomputed(new TreeMap<>(Map.of(
- duration(-50, SECOND), 0.0,
- duration(50, SECOND), 10.0,
- duration(60, SECOND), 30.0,
- duration(90, SECOND), -30.0)));
+ final Resource precomputedWithMultipleSegments;
@Test
void precomputed_with_multiple_segments_interpolates_each_segment_independently() {
assertValueEquals(5.0, precomputedWithMultipleSegments);
diff --git a/examples/streamline-demo/src/main/java/gov/nasa/jpl/aerie/streamline_demo/Mission.java b/examples/streamline-demo/src/main/java/gov/nasa/jpl/aerie/streamline_demo/Mission.java
index ece739efba..03bc47e965 100644
--- a/examples/streamline-demo/src/main/java/gov/nasa/jpl/aerie/streamline_demo/Mission.java
+++ b/examples/streamline-demo/src/main/java/gov/nasa/jpl/aerie/streamline_demo/Mission.java
@@ -1,22 +1,33 @@
package gov.nasa.jpl.aerie.streamline_demo;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem;
+import gov.nasa.jpl.aerie.contrib.streamline.StreamlineSystem.InitArgs;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resource;
import gov.nasa.jpl.aerie.contrib.streamline.debugging.Profiling;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.Registrar;
+import gov.nasa.jpl.aerie.contrib.streamline.modeling.Registration;
import gov.nasa.jpl.aerie.merlin.framework.ModelActions;
+import java.time.Instant;
+
+import static gov.nasa.jpl.aerie.contrib.streamline.modeling.Registration.REGISTRAR;
+
public final class Mission {
public final DataModel dataModel;
public final ErrorTestingModel errorTestingModel;
public final ApproximationModel approximationModel;
- public Mission(final gov.nasa.jpl.aerie.merlin.framework.Registrar registrar$, final Configuration config) {
- var registrar = new Registrar(registrar$, Registrar.ErrorBehavior.Log);
- if (config.traceResources) registrar.setTrace();
+ public Mission(final gov.nasa.jpl.aerie.merlin.framework.Registrar registrar$, Instant planStart, final Configuration config) {
+ StreamlineSystem.init(InitArgs.builder()
+ .baseRegistrar(registrar$)
+ .planStart(planStart)
+ .errorBehavior(Registrar.ErrorBehavior.Log)
+ .build());
+ if (config.traceResources) REGISTRAR.setTrace();
if (config.profileResources) Resource.profileAllResources();
- dataModel = new DataModel(registrar, config);
- errorTestingModel = new ErrorTestingModel(registrar, config);
- approximationModel = new ApproximationModel(registrar, config);
+ dataModel = new DataModel(REGISTRAR, config);
+ errorTestingModel = new ErrorTestingModel(REGISTRAR, config);
+ approximationModel = new ApproximationModel(REGISTRAR, config);
if (config.profilingDumpTime.isPositive()) {
ModelActions.defer(config.profilingDumpTime, Profiling::dump);
}