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: + *

+ * 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); }