From 7881355ffa3cb0d2a854966e8310f691398a1103 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 14 Mar 2024 16:39:08 -0700 Subject: [PATCH 001/108] Procedural scheduling build infrastructure --- procedural/scheduling/MODULE_DOCS.md | 1 + procedural/scheduling/build.gradle | 55 ++++++++++++++++++++++++++++ settings.gradle | 1 + 3 files changed, 57 insertions(+) create mode 100644 procedural/scheduling/MODULE_DOCS.md create mode 100644 procedural/scheduling/build.gradle diff --git a/procedural/scheduling/MODULE_DOCS.md b/procedural/scheduling/MODULE_DOCS.md new file mode 100644 index 0000000000..6caed014a6 --- /dev/null +++ b/procedural/scheduling/MODULE_DOCS.md @@ -0,0 +1 @@ +# Module Scheduling diff --git a/procedural/scheduling/build.gradle b/procedural/scheduling/build.gradle new file mode 100644 index 0000000000..e64e5a8a8e --- /dev/null +++ b/procedural/scheduling/build.gradle @@ -0,0 +1,55 @@ +import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile + +plugins { + id 'com.github.johnrengelman.shadow' version '8.1.1' + id "org.jetbrains.kotlin.jvm" version "1.9.22" + id 'java-library' + id 'org.jetbrains.dokka' version '1.9.10' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation project(':procedural:timeline') + implementation project(':merlin-driver') + + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' + testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +tasks.withType(KotlinJvmCompile.class).configureEach { + jvmTargetValidationMode = JvmTargetValidationMode.WARNING +} + +tasks.named('test') { + useJUnitPlatform() +} + +kotlin { + jvmToolchain(21) +} + +var timelineSource = "${project(":procedural:timeline").projectDir}/src/main/kotlin" + +dokkaHtmlPartial.configure { + dokkaSourceSets { + configureEach { + // used as project name in the header + moduleName.set("Scheduling") + + reportUndocumented.set(true) + failOnWarning.set(true) + + // contains descriptions for the module and the packages + includes.from("MODULE_DOCS.md") + + sourceRoots.from(timelineSource) + suppressedFiles.from(timelineSource) + } + } +} + diff --git a/settings.gradle b/settings.gradle index cd72036376..2b487ff64c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,6 +15,7 @@ include 'permissions' // Procedural post-simulation libraries include 'procedural:timeline' include 'procedural:constraints' +include 'procedural:scheduling' // Services for deployment within the Aerie infrastructure include 'merlin-server' From 576699ab8adcedf34354440bd69d604984684582 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 9 May 2024 14:55:52 -0700 Subject: [PATCH 002/108] Plan edit and simulation interface --- .../jpl/aerie/e2e/TimelineRemoteTests.java | 15 +- procedural/scheduling/build.gradle | 1 + .../aerie/procedural/scheduling/Procedure.kt | 13 + .../aerie/procedural/scheduling/plan/Edit.kt | 8 + .../scheduling/plan/EditablePlan.kt | 16 ++ .../scheduling/plan/NewDirective.kt | 26 ++ .../simulation/CheckpointGeneration.kt | 12 + .../simulation/CheckpointRetention.kt | 9 + .../scheduling/simulation/PauseBehavior.kt | 16 ++ .../scheduling/simulation/SimulateOptions.kt | 7 + .../timeline/payloads/activities/Anchor.kt | 33 +++ .../payloads/activities/AnyDirective.kt | 1 + .../timeline/payloads/activities/Directive.kt | 21 +- .../aerie/timeline/plan/AeriePostgresPlan.kt | 262 +++++------------- .../plan/AeriePostgresSimulationResults.kt | 167 +++++++++++ .../gov/nasa/jpl/aerie/timeline/plan/Plan.kt | 27 -- .../jpl/aerie/timeline/plan/SimulatedPlan.kt | 21 ++ .../aerie/timeline/plan/SimulationResults.kt | 36 +++ .../nasa/jpl/aerie/timeline/plan/SqlUtils.kt | 14 + 19 files changed, 482 insertions(+), 223 deletions(-) create mode 100644 procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Procedure.kt create mode 100644 procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt create mode 100644 procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt create mode 100644 procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt create mode 100644 procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt create mode 100644 procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt create mode 100644 procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt create mode 100644 procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/SimulateOptions.kt create mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Anchor.kt create mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresSimulationResults.kt create mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulatedPlan.kt create mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulationResults.kt create mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SqlUtils.kt diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java index 0abb6e651b..49133b9b8c 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java @@ -11,7 +11,9 @@ import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation; import gov.nasa.jpl.aerie.timeline.payloads.Segment; import gov.nasa.jpl.aerie.timeline.plan.AeriePostgresPlan; +import gov.nasa.jpl.aerie.timeline.plan.AeriePostgresSimulationResults; import gov.nasa.jpl.aerie.timeline.plan.Plan; +import gov.nasa.jpl.aerie.timeline.plan.SimulatedPlan; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -38,9 +40,8 @@ public class TimelineRemoteTests { private int modelId; private int planId; private int activityId; - private int simDatasetId; - private Plan plan; + private SimulatedPlan simulatedPlan; private Connection connection; private HikariDataSource dataSource; @BeforeAll @@ -97,9 +98,11 @@ void beforeEach() throws IOException, InterruptedException { "BiteBanana", "1h", Json.createObjectBuilder().add("biteSize", 1).build()); - simDatasetId = hasura.awaitSimulation(planId).simDatasetId(); + int simDatasetId = hasura.awaitSimulation(planId).simDatasetId(); plan = new AeriePostgresPlan(connection, simDatasetId); + final var simResults = new AeriePostgresSimulationResults(connection, simDatasetId, plan); + simulatedPlan = new SimulatedPlan(plan, simResults); } @AfterEach @@ -110,7 +113,7 @@ void afterEach() throws IOException { @Test void queryActivityInstances() { - final var instances = plan.instances().collect(); + final var instances = simulatedPlan.instances().collect(); assertEquals(1, instances.size()); final var instance = instances.get(0); assertEquals("BiteBanana", instance.getType()); @@ -121,7 +124,7 @@ void queryActivityInstances() { @Test void queryActivityDirectives() { - final var directives = plan.directives().collect(); + final var directives = simulatedPlan.directives().collect(); assertEquals(1, directives.size()); final var directive = directives.get(0); assertEquals("BiteBanana", directive.getType()); @@ -131,7 +134,7 @@ void queryActivityDirectives() { @Test void queryResources() { - final var fruit = plan.resource("/fruit", Real::deserialize).collect(); + final var fruit = simulatedPlan.resource("/fruit", Real::deserialize).collect(); assertIterableEquals( List.of( Segment.of(Interval.betweenClosedOpen(Duration.ZERO, Duration.HOUR), new LinearEquation(4.0)), diff --git a/procedural/scheduling/build.gradle b/procedural/scheduling/build.gradle index e64e5a8a8e..1a7118bf8a 100644 --- a/procedural/scheduling/build.gradle +++ b/procedural/scheduling/build.gradle @@ -15,6 +15,7 @@ repositories { dependencies { implementation project(':procedural:timeline') implementation project(':merlin-driver') + implementation project(':scheduler-driver') testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Procedure.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Procedure.kt new file mode 100644 index 0000000000..9ccdce849a --- /dev/null +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Procedure.kt @@ -0,0 +1,13 @@ +package gov.nasa.jpl.aerie.procedural.scheduling + +import gov.nasa.jpl.aerie.procedural.scheduling.plan.EditablePlan +import gov.nasa.jpl.aerie.timeline.CollectOptions + +interface Procedure { + /** + * Run the procedure. + * + * @param plan A plan representation that can be edited and simulated. + */ + fun run(plan: EditablePlan) +} diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt new file mode 100644 index 0000000000..431142d3a1 --- /dev/null +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt @@ -0,0 +1,8 @@ +package gov.nasa.jpl.aerie.procedural.scheduling.plan + +import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective +import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive + +sealed interface Edit { + data class Create(val directive: Directive): Edit +} diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt new file mode 100644 index 0000000000..280059a89b --- /dev/null +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt @@ -0,0 +1,16 @@ +package gov.nasa.jpl.aerie.procedural.scheduling.plan + +import gov.nasa.jpl.aerie.procedural.scheduling.simulation.SimulateOptions +import gov.nasa.jpl.aerie.timeline.plan.Plan +import gov.nasa.jpl.aerie.timeline.plan.SimulationResults + +interface EditablePlan: Plan { + fun latestResults(): SimulationResults? + + fun create(directive: NewDirective): Long + fun commit() + fun rollback(): List + + fun simulate(options: SimulateOptions): SimulationResults + fun simulate() = simulate(SimulateOptions()) +} diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt new file mode 100644 index 0000000000..8d5ae150e5 --- /dev/null +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt @@ -0,0 +1,26 @@ +package gov.nasa.jpl.aerie.procedural.scheduling.plan + +import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective +import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive +import gov.nasa.jpl.aerie.timeline.payloads.activities.DirectiveStart + +data class NewDirective( + val inner: AnyDirective, + val name: String, + val type: String, + val start: DirectiveStart +) { + fun resolve(id: Long, parent: Directive<*>?): Directive { + if (start is DirectiveStart.Anchor) { + if (parent == null) throw IllegalArgumentException("Parent must provided when anchor is not null") + start.updateEstimate(parent.startTime + start.offset) + } + return Directive( + inner, + name, + id, + type, + start + ) + } +} diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt new file mode 100644 index 0000000000..256b2d3d85 --- /dev/null +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt @@ -0,0 +1,12 @@ +package gov.nasa.jpl.aerie.procedural.scheduling.simulation + +import gov.nasa.jpl.aerie.timeline.Duration + +sealed interface CheckpointGeneration { + data class FixedPeriod(val period: Duration): CheckpointGeneration + data class AtTimes(val times: List): CheckpointGeneration { + constructor(vararg times: Duration): this(times.asList()) + } + data object AtEnd: CheckpointGeneration + data object None: CheckpointGeneration +} diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt new file mode 100644 index 0000000000..d5c904cd3b --- /dev/null +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt @@ -0,0 +1,9 @@ +package gov.nasa.jpl.aerie.procedural.scheduling.simulation + +import gov.nasa.jpl.aerie.timeline.Duration + +sealed interface CheckpointRetention { + data object All: CheckpointRetention + data object Latest: CheckpointRetention + data class DurationFromPresent(val dur: Duration): CheckpointRetention +} diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt new file mode 100644 index 0000000000..8ea928608c --- /dev/null +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt @@ -0,0 +1,16 @@ +package gov.nasa.jpl.aerie.procedural.scheduling.simulation + +import gov.nasa.jpl.aerie.timeline.Duration + +sealed interface PauseBehavior { + data class AfterDuration(val dur: Duration): PauseBehavior + data class AfterActivity(val directive: Long): PauseBehavior + data object AfterNewEdits: PauseBehavior + data object AtEnd: PauseBehavior + + data class EarliestOf(val pausePoints: List): PauseBehavior + data class LatestOf(val pausePoints: List): PauseBehavior + + // very hard! + // data class OnCondition(val condition: ???): PauseBehavior +} diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/SimulateOptions.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/SimulateOptions.kt new file mode 100644 index 0000000000..26e8638b5a --- /dev/null +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/SimulateOptions.kt @@ -0,0 +1,7 @@ +package gov.nasa.jpl.aerie.procedural.scheduling.simulation + +/* data */ class SimulateOptions( +// val checkPointGeneration: CheckpointGeneration = CheckpointGeneration.None, +// val checkpointRetention: CheckpointRetention = CheckpointRetention.All, +// val pause: PauseBehavior = PauseBehavior.AtEnd, +) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Anchor.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Anchor.kt new file mode 100644 index 0000000000..2d2273e1a6 --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Anchor.kt @@ -0,0 +1,33 @@ +package gov.nasa.jpl.aerie.timeline.payloads.activities + +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration + +sealed interface DirectiveStart { + fun atNewTime(time: Duration): DirectiveStart + + data class Anchor( + val parentId: Long, + val offset: Duration, + val anchorPoint: AnchorPoint, + var estimatedStart: Duration + ): DirectiveStart { + enum class AnchorPoint { + Start, End; + + companion object { + fun anchorToStart(b: Boolean) = if (b) Start else End + } + } + + constructor(parentId: Long, offset: Duration, anchorPoint: AnchorPoint): this(parentId, offset, anchorPoint, Duration.ZERO) + + fun updateEstimate(d: Duration) { + estimatedStart = d + } + override fun atNewTime(time: Duration) = Anchor(parentId, offset + time - estimatedStart, anchorPoint, time) + } + + data class Absolute(val time: Duration): DirectiveStart { + override fun atNewTime(time: Duration) = Absolute(time) + } +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyDirective.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyDirective.kt index 14f4ee11f7..55c26650a8 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyDirective.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyDirective.kt @@ -7,6 +7,7 @@ import kotlin.jvm.optionals.getOrNull data class AnyDirective( /***/ @JvmField val arguments: Map ) { + fun serialize(): SerializedValue = SerializedValue.of(arguments) /***/ companion object { /** Converts a [SerializedValue] object containing activity arguments into an [AnyDirective] object. */ @JvmStatic fun deserialize(attributes: SerializedValue) = AnyDirective(attributes.asMap().getOrNull()!!) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt index 971e91a3fb..4d93d8389f 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt @@ -15,13 +15,30 @@ data class Directive( @JvmField val id: Long, override val type: String, - override val startTime: Duration + + val start: DirectiveStart, ): Activity> { + override val startTime: Duration + get() = when (start) { + is DirectiveStart.Absolute -> start.time + is DirectiveStart.Anchor -> start.estimatedStart + } + override val interval: Interval get() = Interval.at(startTime) override fun withNewInterval(i: Interval): Directive { - if (i.isPoint()) return Directive(inner, name, id, type, i.start) + if (i.isPoint()) return Directive(inner, name, id, type, start.atNewTime(i.start)) else throw Exception("Cannot change directive time to a non-instantaneous interval.") } + + fun mapInner(f: (A) -> R) = Directive( + f(inner), + name, + id, + type, + start + ) + + fun withNewAnchor(anchor: DirectiveStart.Anchor) = Directive(inner, name, id, type, anchor) } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresPlan.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresPlan.kt index 3bd88f2118..581c314621 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresPlan.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresPlan.kt @@ -3,229 +3,115 @@ package gov.nasa.jpl.aerie.timeline.plan import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.timeline.BaseTimeline -import gov.nasa.jpl.aerie.timeline.Duration -import gov.nasa.jpl.aerie.timeline.Duration.Companion.minus -import gov.nasa.jpl.aerie.timeline.Duration.Companion.plus -import gov.nasa.jpl.aerie.timeline.Interval.Companion.at +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.timeline.BoundsTransformer +import gov.nasa.jpl.aerie.timeline.durationUtils.* import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.* -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective -import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyInstance import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive -import gov.nasa.jpl.aerie.timeline.payloads.activities.Instance import gov.nasa.jpl.aerie.timeline.collections.Directives -import gov.nasa.jpl.aerie.timeline.collections.Instances -import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegmentsOp +import gov.nasa.jpl.aerie.timeline.payloads.activities.DirectiveStart import java.io.StringReader import java.sql.Connection -import java.sql.PreparedStatement import java.time.Instant import javax.json.Json -import kotlin.jvm.optionals.getOrNull /** A connection to Aerie's database for a particular simulation result. */ -data class AeriePostgresPlan( +class AeriePostgresPlan( /** A connection to Aerie's database. */ - val c: Connection, - /** The particular simulation dataset to query. */ - val simDatasetId: Int + private val c: Connection, + private val id: Int ): Plan { - private val datasetId by lazy { - val statement = c.prepareStatement("select dataset_id from merlin.simulation_dataset where id = ?;") - statement.setInt(1, simDatasetId) - getSingleIntQueryResult(statement) - } - - private val simulationId by lazy { - val statement = c.prepareStatement("select simulation_id from merlin.simulation_dataset where id = ?;") - statement.setInt(1, simDatasetId) - getSingleIntQueryResult(statement) - } - - private val simulationInfo by lazy { - val statement = c.prepareStatement("select plan_id, simulation_start_time, simulation_end_time from merlin.simulation where id = ?;") - statement.setInt(1, simulationId) - val response = statement.executeQuery() - if (!response.next()) throw DatabaseError("Expected exactly one result for query, found none: $statement") - val result = object { - val planId = response.getInt(1) - val startTime = response.getTimestamp(2).toInstant() - val endTime = response.getTimestamp(3).toInstant() - } - if (response.next()) throw DatabaseError("Expected exactly one result for query, found more than one: $statement") - result - } - - private fun getSingleIntQueryResult(statement: PreparedStatement): Int { - val result = statement.executeQuery() - if (!result.next()) throw DatabaseError("Expected exactly one result for query, found none: $statement") - val int = result.getInt(1) - if (result.next()) throw DatabaseError("Expected exactly one result for query, found more than one: $statement") - return int - } - private val planInfo by lazy { val statement = c.prepareStatement("select start_time, duration from merlin.plan where id = ?;") - statement.setInt(1, simulationInfo.planId) - intervalStyleStatement.execute() + statement.setInt(1, id) + intervalStyleStatement(c).execute() val response = statement.executeQuery() if (!response.next()) throw DatabaseError("Expected exactly one result for query, found none: $statement") val result = object { val startTime = response.getTimestamp(1).toInstant() val duration = Duration.parseISO8601(response.getString(2)) - val id = simulationInfo.planId + val id = this@AeriePostgresPlan.id } if (response.next()) throw DatabaseError("Expected exactly one result for query, found more than one: $statement") result } override fun totalBounds() = between(Duration.ZERO, planInfo.duration) - override fun simBounds() = between( - toRelative(simulationInfo.startTime), - toRelative(simulationInfo.endTime), - ) override fun toRelative(abs: Instant) = abs - planInfo.startTime override fun toAbsolute(rel: Duration) = planInfo.startTime + rel - private val intervalStyleStatement = c.prepareStatement("set intervalstyle = 'iso_8601';") - private val profileInfoStatement = c.prepareStatement( - "select id, duration from merlin.profile where dataset_id = ? and name = ?;" - ) - private data class ProfileInfo(val id: Int, val duration: Duration) - - private val segmentsStatement = c.prepareStatement( - "select start_offset, dynamics, is_gap from merlin.profile_segment where profile_id = ? and dataset_id = ? order by start_offset asc;" - ) /***/ class DatabaseError(message: String): Error(message) - override fun > resource(name: String, ctor: (List>) -> TL): TL { - val profileInfo = getProfileInfo(name) - - segmentsStatement.clearParameters() - segmentsStatement.setInt(1, profileInfo.id) - segmentsStatement.setInt(2, datasetId) - intervalStyleStatement.execute() - val response = segmentsStatement.executeQuery() - - val result = mutableListOf>() - - var previousValue: SerializedValue? = null - var previousStart: Duration? = null - - while (response.next()) { - val thisStart = Duration.parseISO8601(response.getString(1)) - if (previousStart !== null) { - val interval = between(previousStart, thisStart, Inclusive, Exclusive) - val newSegment = Segment( - interval, - previousValue!! - ) - result.add(newSegment) - } - if (!response.getBoolean(3)) { // if not gap - val serializedValue = parseJson(response.getString(2)) - previousValue = serializedValue - previousStart = thisStart - } else { - previousValue = null - previousStart = null - } - } - if (previousStart !== null) { - val interval = between(previousStart, profileInfo.duration, Inclusive, Exclusive) - result.add( - Segment( - interval, - previousValue!! - ) - ) - } - return ctor(result) - } - - private fun getProfileInfo(name: String): ProfileInfo { - profileInfoStatement.clearParameters() - profileInfoStatement.setInt(1, datasetId) - profileInfoStatement.setString(2, name) - intervalStyleStatement.execute() - val profileResult = profileInfoStatement.executeQuery() - if (!profileResult.next()) throw DatabaseError("Profile $name not found in database") - val id = profileResult.getInt(1) - val duration = Duration.parseISO8601(profileResult.getString(2)) - if (profileResult.next()) throw DatabaseError("Multiple profiles named $name found in one simulation dataset") - return ProfileInfo(id, duration) - } - private fun parseJson(jsonStr: String): SerializedValue = Json.createReader(StringReader(jsonStr)).use { reader -> - val requestJson = reader.readValue() - val result = SerializedValueJsonParser.serializedValueP.parse(requestJson) - return result.getSuccessOrThrow { DatabaseError(it.toString()) } - } - - private val allInstancesStatement = c.prepareStatement( - "select start_offset, duration, attributes, activity_type_name, id from merlin.simulated_activity" + - " where simulation_dataset_id = ?;" - ) - private val filteredInstancesStatement = c.prepareStatement( - "select start_offset, duration, attributes, activity_type_name, id from merlin.simulated_activity" + - " where simulation_dataset_id = ? and activity_type_name = ?;" - ) - override fun instances(type: String?, deserializer: (SerializedValue) -> A): Instances { - val statement = if (type == null) allInstancesStatement else filteredInstancesStatement - statement.clearParameters() - statement.setInt(1, simDatasetId) - if (type != null) statement.setString(2, type); - intervalStyleStatement.execute() - val response = statement.executeQuery() - val result = mutableListOf>() - while (response.next()) { - val start = Duration.parseISO8601(response.getString(1)) - val id = response.getLong(5) - val attributesString = response.getString(3) - val attributes = parseJson(attributesString) - val directiveId = attributes.asMap().getOrNull()?.get("directiveId")?.asInt()?.getOrNull() - result.add(Instance( - deserializer(attributes), - response.getString(4), - id, - directiveId, - between(start, start.plus(Duration.parseISO8601(response.getString(2)))) - )) - } - return Instances(result) + val requestJson = reader.readValue() + val result = SerializedValueJsonParser.serializedValueP.parse(requestJson) + return result.getSuccessOrThrow { DatabaseError(it.toString()) } } private val allDirectivesStatement = c.prepareStatement( - "select name, start_offset, type, arguments, id from merlin.activity_directive where plan_id = ?" + - " and start_offset > ?::interval and start_offset < ?::interval;" - ) - private val filteredDirectivesStatement = c.prepareStatement( - "select name, start_offset, type, arguments, id from merlin.activity_directive where plan_id = ?" + - " and start_offset > ?::interval and start_offset < ?::interval and type = ?;" + "select name, start_offset, type, arguments, id, anchor_id, anchored_to_start from merlin.activity_directive where plan_id = ?" + + " and start_offset > ?::interval and start_offset < ?::interval;" ) - override fun directives(type: String?, deserializer: (SerializedValue) -> A) = BaseTimeline(::Directives) { opts -> - val statement = if (type == null) allDirectivesStatement else filteredDirectivesStatement - statement.clearParameters() - statement.setInt(1, planInfo.id) - statement.setString(2, opts.bounds.start.toISO8601()) - statement.setString(3, opts.bounds.end.toISO8601()) - if (type != null) statement.setString(4, type) - intervalStyleStatement.execute() - val response = statement.executeQuery() - val result = mutableListOf>() - while (response.next()) { - result.add(Directive( - deserializer(parseJson(response.getString(4))), - response.getString(1), - response.getLong(5), - response.getString(3), - Duration.parseISO8601(response.getString(2)) - )) - } - result - }.specialize() + override fun directives(type: String?, deserializer: (SerializedValue) -> A) = + Directives( + ( + if (type == null) allDirectives + else allDirectives.filter { it.type == type } + ) + ).unsafeMap(::Directives, BoundsTransformer.IDENTITY, false) { + it.mapInner(deserializer) + } + + private val allDirectives by lazy { + BaseTimeline(::Directives) { opts -> + allDirectivesStatement.clearParameters() + allDirectivesStatement.setInt(1, planInfo.id) + allDirectivesStatement.setString(2, opts.bounds.start.toISO8601()) + allDirectivesStatement.setString(3, opts.bounds.end.toISO8601()) + intervalStyleStatement(c).execute() + val response = allDirectivesStatement.executeQuery() + var unresolved = mutableListOf>() + while (response.next()) { + val anchorId = response.getLong(6) + val offset = Duration.parseISO8601(response.getString(2)) + val start = if (anchorId != 0L) { // this means SQL null. Terrible interface imo + val anchoredToStart = response.getBoolean(7) + DirectiveStart.Anchor(anchorId, offset, if (anchoredToStart) DirectiveStart.Anchor.AnchorPoint.Start else DirectiveStart.Anchor.AnchorPoint.End, Duration.ZERO) + } else DirectiveStart.Absolute(offset) + unresolved.add(Directive( + parseJson(response.getString(4)), + response.getString(1), + response.getLong(5), + response.getString(3), + start + )) + } + val result = mutableListOf>() + while (unresolved.size != 0) { + val sizeAtStartOfStep = unresolved.size + unresolved = unresolved.filterNot { + when (it.start) { + is DirectiveStart.Absolute -> { + result.add(it) + } + is DirectiveStart.Anchor -> { + val index = result.binarySearch { a -> a.id.compareTo(it.start.parentId) } + if (index >= 0) { + val parent = result[index] + it.start.updateEstimate(parent.startTime + it.start.offset) + result.add(it) + } else false + } + } + }.toMutableList() + if (sizeAtStartOfStep == unresolved.size) throw Error("Cannot resolve anchors: $unresolved") + result.sortBy { it.id } + } + result + }.specialize() + .collect() + } } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresSimulationResults.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresSimulationResults.kt new file mode 100644 index 0000000000..b1222a01d4 --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresSimulationResults.kt @@ -0,0 +1,167 @@ +package gov.nasa.jpl.aerie.timeline.plan + +import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue +import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.timeline.Interval.Companion.between +import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive +import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive +import gov.nasa.jpl.aerie.timeline.collections.Instances +import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegmentsOp +import gov.nasa.jpl.aerie.timeline.payloads.Segment +import gov.nasa.jpl.aerie.timeline.payloads.activities.Instance +import java.io.StringReader +import java.sql.Connection +import javax.json.Json +import kotlin.jvm.optionals.getOrNull + +/** A connection to Aerie's database for a particular simulation result. */ +data class AeriePostgresSimulationResults( + /** A connection to Aerie's database. */ + private val c: Connection, + /** The particular simulation dataset to query. */ + private val simDatasetId: Int, + private val plan: Plan, + private val stale: Boolean +): SimulationResults { + + private val datasetId by lazy { + val statement = c.prepareStatement("select dataset_id from merlin.simulation_dataset where id = ?;") + statement.setInt(1, simDatasetId) + getSingleLongQueryResult(statement) + } + + private val simulationId by lazy { + val statement = c.prepareStatement("select simulation_id from merlin.simulation_dataset where id = ?;") + statement.setInt(1, simDatasetId) + getSingleLongQueryResult(statement) + } + + private val simulationInfo by lazy { + val statement = c.prepareStatement("select simulation_start_time, simulation_end_time from merlin.simulation where id = ?;") + statement.setLong(1, simulationId) + val response = statement.executeQuery() + if (!response.next()) throw DatabaseError("Expected exactly one result for query, found none: $statement") + val result = object { + val startTime = response.getTimestamp(1).toInstant() + val endTime = response.getTimestamp(2).toInstant() + } + if (response.next()) throw DatabaseError("Expected exactly one result for query, found more than one: $statement") + result + } + + override fun simBounds() = between( + plan.toRelative(simulationInfo.startTime), + plan.toRelative(simulationInfo.endTime), + ) + + private val profileInfoStatement = c.prepareStatement( + "select id, duration from merlin.profile where dataset_id = ? and name = ?;" + ) + private data class ProfileInfo(val id: Int, val duration: Duration) + + private val segmentsStatement = c.prepareStatement( + "select start_offset, dynamics, is_gap from merlin.profile_segment where profile_id = ? and dataset_id = ? order by start_offset asc;" + ) + + /***/ class DatabaseError(message: String): Error(message) + + override fun > resource(name: String, deserializer: (List>) -> TL): TL { + val profileInfo = getProfileInfo(name) + + segmentsStatement.clearParameters() + segmentsStatement.setInt(1, profileInfo.id) + segmentsStatement.setLong(2, datasetId) + intervalStyleStatement(c).execute() + val response = segmentsStatement.executeQuery() + + val result = mutableListOf>() + + var previousValue: SerializedValue? = null + var previousStart: Duration? = null + + while (response.next()) { + val thisStart = Duration.parseISO8601(response.getString(1)) + if (previousStart !== null) { + val interval = between(previousStart, thisStart, Inclusive, Exclusive) + val newSegment = Segment( + interval, + previousValue!! + ) + result.add(newSegment) + } + if (!response.getBoolean(3)) { // if not gap + val serializedValue = parseJson(response.getString(2)) + previousValue = serializedValue + previousStart = thisStart + } else { + previousValue = null + previousStart = null + } + } + if (previousStart !== null) { + val interval = between(previousStart, profileInfo.duration, Inclusive, Exclusive) + result.add( + Segment( + interval, + previousValue!! + ) + ) + } + return deserializer(result) + } + + private fun getProfileInfo(name: String): ProfileInfo { + profileInfoStatement.clearParameters() + profileInfoStatement.setLong(1, datasetId) + profileInfoStatement.setString(2, name) + intervalStyleStatement(c).execute() + val profileResult = profileInfoStatement.executeQuery() + if (!profileResult.next()) throw DatabaseError("Profile $name not found in database") + val id = profileResult.getInt(1) + val duration = Duration.parseISO8601(profileResult.getString(2)) + if (profileResult.next()) throw DatabaseError("Multiple profiles named $name found in one simulation dataset") + return ProfileInfo(id, duration) + } + + private fun parseJson(jsonStr: String): SerializedValue = Json.createReader(StringReader(jsonStr)).use { reader -> + val requestJson = reader.readValue() + val result = SerializedValueJsonParser.serializedValueP.parse(requestJson) + return result.getSuccessOrThrow { DatabaseError(it.toString()) } + } + + private val allInstancesStatement = c.prepareStatement( + "select start_offset, duration, attributes, activity_type_name, id from merlin.simulated_activity" + + " where simulation_dataset_id = ?;" + ) + private val filteredInstancesStatement = c.prepareStatement( + "select start_offset, duration, attributes, activity_type_name, id from merlin.simulated_activity" + + " where simulation_dataset_id = ? and activity_type_name = ?;" + ) + override fun instances(type: String?, deserializer: (SerializedValue) -> A): Instances { + val statement = if (type == null) allInstancesStatement else filteredInstancesStatement + statement.clearParameters() + statement.setInt(1, simDatasetId) + if (type != null) statement.setString(2, type); + intervalStyleStatement(c).execute() + val response = statement.executeQuery() + val result = mutableListOf>() + while (response.next()) { + val start = Duration.parseISO8601(response.getString(1)) + val id = response.getLong(5) + val attributesString = response.getString(3) + val attributes = parseJson(attributesString) + val directiveId = attributes.asMap().getOrNull()?.get("directiveId")?.asInt()?.getOrNull() + result.add(Instance( + deserializer(attributes), + response.getString(4), + id, + directiveId, + between(start, start.plus(Duration.parseISO8601(response.getString(2)))) + )) + } + return Instances(result) + } + + override fun isStale() = stale +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/Plan.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/Plan.kt index a0c0452e32..fa0034a9a3 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/Plan.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/Plan.kt @@ -3,12 +3,8 @@ package gov.nasa.jpl.aerie.timeline.plan import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.timeline.Duration import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective -import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyInstance -import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegmentsOp import gov.nasa.jpl.aerie.timeline.collections.Directives -import gov.nasa.jpl.aerie.timeline.collections.Instances import java.time.Instant /** An interface for querying plan information and simulation results. */ @@ -16,34 +12,11 @@ interface Plan { /** Total extent of the plan's bounds, whether it was simulated on the full extent or not. */ fun totalBounds(): Interval - /** Bounds on which the plan was most recently simulated. */ - fun simBounds(): Interval - /** Convert a time instant to a relative duration (relative to plan start). */ fun toRelative(abs: Instant): Duration /** Convert a relative duration to a time instant. */ fun toAbsolute(rel: Duration): Instant - /** - * Query a resource profile from the database - * - * @param ctor constructor of the profile, converting [SerializedValue] - * @param name string name of the resource - */ - fun > resource(name: String, ctor: (List>) -> TL): TL - - /** - * Query activity instances. - * - * @param type Activity type name to filter by; queries all activities if null. - * @param deserializer a function from [SerializedValue] to an inner payload type - */ - fun instances(type: String?, deserializer: (SerializedValue) -> A): Instances - /** Queries activity instances, filtered by type, deserializing them as [AnyInstance]. **/ - fun instances(type: String) = instances(type, AnyInstance::deserialize) - /** Queries all activity instances, deserializing them as [AnyInstance]. **/ - fun instances() = instances(null, AnyInstance::deserialize) - /** * Query activity directives. * diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulatedPlan.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulatedPlan.kt new file mode 100644 index 0000000000..7c27aa4132 --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulatedPlan.kt @@ -0,0 +1,21 @@ +package gov.nasa.jpl.aerie.timeline.plan + +import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue +import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.timeline.Interval.Companion.between +import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.* +import gov.nasa.jpl.aerie.timeline.payloads.Segment +import gov.nasa.jpl.aerie.timeline.payloads.activities.Instance +import gov.nasa.jpl.aerie.timeline.collections.Instances +import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegmentsOp +import java.io.StringReader +import java.sql.PreparedStatement +import javax.json.Json +import kotlin.jvm.optionals.getOrNull + +/** A connection to Aerie's database for a particular simulation result. */ +data class SimulatedPlan( + private val plan: Plan, + private val simResults: SimulationResults +): Plan by plan, SimulationResults by simResults diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulationResults.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulationResults.kt new file mode 100644 index 0000000000..77bf472118 --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulationResults.kt @@ -0,0 +1,36 @@ +package gov.nasa.jpl.aerie.timeline.plan + +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue +import gov.nasa.jpl.aerie.timeline.Interval +import gov.nasa.jpl.aerie.timeline.payloads.Segment +import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyInstance +import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegmentsOp +import gov.nasa.jpl.aerie.timeline.collections.Instances + +/** An interface for querying plan information and simulation results. */ +interface SimulationResults { + fun isStale(): Boolean + + /** Bounds on which the plan was most recently simulated. */ + fun simBounds(): Interval + + /** + * Query a resource profile from the database + * + * @param deserializer constructor of the profile, converting [SerializedValue] + * @param name string name of the resource + */ + fun > resource(name: String, deserializer: (List>) -> TL): TL + + /** + * Query activity instances. + * + * @param type Activity type name to filter by; queries all activities if null. + * @param deserializer a function from [SerializedValue] to an inner payload type + */ + fun instances(type: String?, deserializer: (SerializedValue) -> A): Instances + /** Queries activity instances, filtered by type, deserializing them as [AnyInstance]. **/ + fun instances(type: String) = instances(type, AnyInstance::deserialize) + /** Queries all activity instances, deserializing them as [AnyInstance]. **/ + fun instances() = instances(null, AnyInstance::deserialize) +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SqlUtils.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SqlUtils.kt new file mode 100644 index 0000000000..94c220f0d9 --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SqlUtils.kt @@ -0,0 +1,14 @@ +package gov.nasa.jpl.aerie.timeline.plan + +import java.sql.Connection +import java.sql.PreparedStatement + +fun getSingleLongQueryResult(statement: PreparedStatement): Long { + val result = statement.executeQuery() + if (!result.next()) throw AeriePostgresSimulationResults.DatabaseError("Expected exactly one result for query, found none: $statement") + val int = result.getLong(1) + if (result.next()) throw AeriePostgresSimulationResults.DatabaseError("Expected exactly one result for query, found more than one: $statement") + return int +} + +fun intervalStyleStatement(c: Connection): PreparedStatement = c.prepareStatement("set intervalstyle = 'iso_8601';") From 43ff11b59259f8ca25dc1f07e378148be4437582 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Wed, 7 Aug 2024 06:38:37 -0700 Subject: [PATCH 003/108] Use SimulatedPlan in Constraint.kt --- .../gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt b/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt index 2fbd717b9b..01e992f7c7 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.procedural.constraints import gov.nasa.jpl.aerie.timeline.CollectOptions -import gov.nasa.jpl.aerie.timeline.plan.Plan +import gov.nasa.jpl.aerie.timeline.plan.SimulatedPlan /** The interface that all constraints must satisfy. */ interface Constraint { @@ -14,5 +14,5 @@ interface Constraint { * @param plan the plan to check the constraint on * @param options the [CollectOptions] that the result will be collected with */ - fun run(plan: Plan, options: CollectOptions): Violations + fun run(plan: SimulatedPlan, options: CollectOptions): Violations } From f0b1c7b0bcda337865ae97df53bb950bbae97227 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 9 May 2024 15:04:11 -0700 Subject: [PATCH 004/108] Remove timeline duration duplicate --- .../jpl/aerie/e2e/TimelineRemoteTests.java | 9 +- .../aerie/merlin/protocol/types/Duration.java | 128 +++++---- .../simulation/CheckpointGeneration.kt | 2 +- .../simulation/CheckpointRetention.kt | 2 +- .../scheduling/simulation/PauseBehavior.kt | 2 +- .../jpl/aerie/timeline/BoundsTransformer.kt | 2 + .../gov/nasa/jpl/aerie/timeline/Duration.kt | 261 ------------------ .../gov/nasa/jpl/aerie/timeline/Interval.kt | 2 + .../timeline/collections/profiles/Booleans.kt | 6 +- .../timeline/collections/profiles/Numbers.kt | 9 +- .../timeline/collections/profiles/Real.kt | 4 +- .../nasa/jpl/aerie/timeline/ops/BooleanOps.kt | 3 +- .../nasa/jpl/aerie/timeline/ops/GeneralOps.kt | 1 + .../aerie/timeline/ops/NonZeroDurationOps.kt | 3 +- .../jpl/aerie/timeline/ops/ParallelOps.kt | 2 + .../aerie/timeline/ops/numeric/LinearOps.kt | 4 +- .../timeline/ops/numeric/SerialNumericOps.kt | 2 +- .../aerie/timeline/payloads/LinearEquation.kt | 3 +- .../timeline/payloads/activities/Activity.kt | 2 +- .../timeline/payloads/activities/Directive.kt | 2 +- .../timeline/payloads/activities/Instance.kt | 2 +- .../plan/AeriePostgresSimulationResults.kt | 2 +- .../gov/nasa/jpl/aerie/timeline/plan/Plan.kt | 2 +- .../jpl/aerie/timeline/plan/SimulatedPlan.kt | 14 - .../aerie/timeline/util/duration/Duration.kt | 37 +++ .../aerie/timeline/BoundsTransformerTest.kt | 2 +- .../nasa/jpl/aerie/timeline/IntervalTest.kt | 5 +- .../nasa/jpl/aerie/timeline/SegmentTest.kt | 2 +- .../aerie/timeline/collections/WindowsTest.kt | 8 +- .../collections/profiles/BooleansTest.kt | 4 +- .../collections/profiles/NumbersTest.kt | 4 +- .../timeline/collections/profiles/RealTest.kt | 3 +- .../jpl/aerie/timeline/ops/GeneralOpsTest.kt | 7 +- .../timeline/ops/NonZeroDurationOpsTest.kt | 3 +- .../jpl/aerie/timeline/ops/ParallelOpsTest.kt | 3 +- .../aerie/timeline/ops/SerialConstantTest.kt | 3 +- .../timeline/ops/SerialSegmentOpsTest.kt | 5 +- .../timeline/ops/numeric/LinearOpsTest.kt | 3 +- .../ops/numeric/PrimitiveNumberOpsTest.kt | 2 +- .../ops/numeric/SerialNumericOpsTest.kt | 4 +- .../jpl/aerie/timeline/util/CoalesceTest.kt | 3 +- .../aerie/timeline/util/LinearEquationTest.kt | 4 +- .../aerie/timeline/util/ListCollectorTest.kt | 3 +- .../nasa/jpl/aerie/timeline/util/Map2Test.kt | 3 +- 44 files changed, 201 insertions(+), 376 deletions(-) delete mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Duration.kt create mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/duration/Duration.kt diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java index 49133b9b8c..37c7408fcb 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java @@ -5,14 +5,13 @@ import com.zaxxer.hikari.HikariDataSource; import gov.nasa.jpl.aerie.e2e.utils.GatewayRequests; import gov.nasa.jpl.aerie.e2e.utils.HasuraRequests; -import gov.nasa.jpl.aerie.timeline.Duration; +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; import gov.nasa.jpl.aerie.timeline.Interval; import gov.nasa.jpl.aerie.timeline.collections.profiles.Real; import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation; import gov.nasa.jpl.aerie.timeline.payloads.Segment; import gov.nasa.jpl.aerie.timeline.plan.AeriePostgresPlan; import gov.nasa.jpl.aerie.timeline.plan.AeriePostgresSimulationResults; -import gov.nasa.jpl.aerie.timeline.plan.Plan; import gov.nasa.jpl.aerie.timeline.plan.SimulatedPlan; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -100,8 +99,10 @@ void beforeEach() throws IOException, InterruptedException { Json.createObjectBuilder().add("biteSize", 1).build()); int simDatasetId = hasura.awaitSimulation(planId).simDatasetId(); - plan = new AeriePostgresPlan(connection, simDatasetId); - final var simResults = new AeriePostgresSimulationResults(connection, simDatasetId, plan); + // Connect to the database + + final var plan = new AeriePostgresPlan(connection, planId); + final var simResults = new AeriePostgresSimulationResults(connection, simDatasetId, plan, false); simulatedPlan = new SimulatedPlan(plan, simResults); } diff --git a/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java b/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java index 42e7a90fca..de287d2efc 100644 --- a/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java +++ b/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java @@ -1,5 +1,6 @@ package gov.nasa.jpl.aerie.merlin.protocol.types; +import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -38,8 +39,8 @@ * *

A time value is represented as a {@code long} where an increment maps to number a specific time unit. Currently, * the underlying time unit is microseconds, however, one should not rely on this always being the case. The maximum - * value of a fixed-point type is simply the largest value that can be represented by the underlying integer type. - * For a {@code long} this yields a range of (-2^63) to (2^63 - 1), or almost 600,000 years, at microsecond + * value of a fixed-point type is the half of the largest value that can be represented by the underlying integer type. + * For a {@code long} this yields a range of -2^62 to 2^62, or about 146,000 years in the future and past, at microsecond * resolution.

*

@@ -95,16 +96,7 @@ * within your own domain in terms of the precise units we give here. *

*/ -public final class Duration implements Comparable { - // Range of (-2^63) to (2^63 - 1) microseconds. - // This comes out to almost 600,000 years, at microsecond resolution. - // Merlin was not designed for time scales longer than this. - private final long durationInMicroseconds; - - private Duration(final long durationInMicroseconds) { - this.durationInMicroseconds = durationInMicroseconds; - } - +public record Duration(long micros) implements Comparable { /** * The smallest observable span of time between instants. * @@ -121,25 +113,34 @@ private Duration(final long durationInMicroseconds) { */ public static final Duration ZERO = new Duration(0); + /* + * The bounds of the duration type are equal to half of long-min and long-max microseconds. This is for two reasons: + * 1. It may avoid some overflow errors by giving a 2x margin. + * 2. Java durations are serialized to ISO8601 duration format using only hours, minutes, and fractional seconds. + * This is to avoid timekeeping issues related to leap seconds and leap years. Postgres intervals can represent + * durations up to hundreds of millions of years, but only by using days and months. Thus, attempting to + * interact with Postgres using a duration of long-min or long-max microseconds, represented as hours, causes an overflow. + * However, long-min / 2 and long-max / 2 microseconds are small enough to not cause overflow. + */ + + private static final long MIN_VALUE_MICROSECONDS = Long.MIN_VALUE / 2; + private static final long MAX_VALUE_MICROSECONDS = Long.MAX_VALUE / 2; + + + /** - * The largest observable negative span of time. Attempting to go "more negative" will cause an exception. + * The largest valid negative span of time. Attempting to go "more negative" may cause an exception. * - *

- * The value of this quantity should not be assumed. - * Currently, this is precisely -9,223,372,036,854,775,808 microseconds, or approximately -293,274 years. - *

+ * The value of this quantity should not be assumed. Currently, it equals half of long-min microseconds, or -146,135 years. */ - public static final Duration MIN_VALUE = new Duration(Long.MIN_VALUE); + public static final Duration MIN_VALUE = new Duration(MIN_VALUE_MICROSECONDS); /** * The largest observable positive span of time. Attempting to go "more positive" will cause an exception. * - *

- * The value of this quantity should not be assumed. - * Currently, this is precisely +9,223,372,036,854,775,807 microseconds, or approximately 293,274 years. - *

+ * The value of this quantity should not be assumed. Currently, it equals half of long-max microseconds, or 146,135 years. */ - public static final Duration MAX_VALUE = new Duration(Long.MAX_VALUE); + public static final Duration MAX_VALUE = new Duration(MAX_VALUE_MICROSECONDS); /** One microsecond (μs). */ public static final Duration MICROSECOND = new Duration(1); @@ -151,6 +152,7 @@ private Duration(final long durationInMicroseconds) { public static final Duration MINUTE = SECOND.times(60); /** One hour (h), equal to 60m. */ public static final Duration HOUR = MINUTE.times(60); + public static final Duration DAY = HOUR.times(24); /** The unit of measurement for microseconds. */ public static final Duration MICROSECONDS = MICROSECOND; @@ -162,6 +164,26 @@ private Duration(final long durationInMicroseconds) { public static final Duration MINUTES = MINUTE; /** The unit of measurement for hours. */ public static final Duration HOURS = HOUR; + public static final Duration DAYS = DAY; + + public static Duration microseconds(final long quantity) throws ArithmeticException { + return MICROSECOND.times(quantity); + } + public static Duration milliseconds(final long quantity) throws ArithmeticException { + return MILLISECOND.times(quantity); + } + public static Duration seconds(final long quantity) throws ArithmeticException { + return SECOND.times(quantity); + } + public static Duration minutes(final long quantity) throws ArithmeticException { + return MINUTE.times(quantity); + } + public static Duration hours(final long quantity) throws ArithmeticException { + return HOUR.times(quantity); + } + public static Duration days(final long quantity) throws ArithmeticException { + return DAY.times(quantity); + } /** Construct a duration in terms of a multiple of some unit. */ public static Duration of(final long quantity, final Duration unit) { @@ -254,7 +276,17 @@ public static Duration roundNearest(final double quantity, final Duration unit) */ public static Duration negate(final Duration duration) throws ArithmeticException { // amusingly, -MIN_VALUE = MIN_VALUE in 2's complement -- `multiplyExact` will correctly fail out in that case. - return new Duration(Math.multiplyExact(-1, duration.durationInMicroseconds)); + return new Duration(Math.multiplyExact(-1, duration.micros)); + } + + /** + * Flip the temporal direction of this duration. A duration into the past becomes one into the future, and vice versa. + * + * @return A new duration with its temporal direction flipped. + * @throws ArithmeticException If the input duration is {@link #MIN_VALUE}. + */ + public Duration negate() throws ArithmeticException { + return Duration.negate(this); } /** @@ -266,19 +298,19 @@ public static Duration negate(final Duration duration) throws ArithmeticExceptio * @throws ArithmeticException If the result would be less than {@link #MIN_VALUE} or greater than {@link #MAX_VALUE}. */ public static Duration add(final Duration left, final Duration right) throws ArithmeticException { - return new Duration(Math.addExact(left.durationInMicroseconds, right.durationInMicroseconds)); + return new Duration(Math.addExact(left.micros, right.micros)); } public static Duration saturatingAdd(final Duration left, final Duration right) { - return new Duration(saturatingAddInternal(left.durationInMicroseconds, right.durationInMicroseconds)); + return new Duration(saturatingAddInternal(left.micros, right.micros)); } private static long saturatingAddInternal(final long left, final long right) { long result = left + right; if (((result ^ left) & (result ^ right)) < 0) { - return Long.MIN_VALUE - (result >>> (Long.SIZE - 1)); + return Math.clamp(Long.MIN_VALUE - (result >>> (Long.SIZE - 1)), MIN_VALUE_MICROSECONDS, MAX_VALUE_MICROSECONDS); } - return result; + return Math.clamp(result, MIN_VALUE_MICROSECONDS, MAX_VALUE_MICROSECONDS); } /** @@ -290,7 +322,7 @@ private static long saturatingAddInternal(final long left, final long right) { * @throws ArithmeticException If the result would be less than {@link #MIN_VALUE} or greater than {@link #MAX_VALUE}. */ public static Duration subtract(final Duration left, final Duration right) throws ArithmeticException { - return new Duration(Math.subtractExact(left.durationInMicroseconds, right.durationInMicroseconds)); + return new Duration(Math.subtractExact(left.micros, right.micros)); } /** @@ -302,7 +334,7 @@ public static Duration subtract(final Duration left, final Duration right) throw * @throws ArithmeticException If the result would be less than {@link #MIN_VALUE} or greater than {@link #MAX_VALUE}. */ public static Duration multiply(final long scalar, final Duration unit) throws ArithmeticException { - return new Duration(Math.multiplyExact(scalar, unit.durationInMicroseconds)); + return new Duration(Math.multiplyExact(scalar, unit.micros)); } /** @@ -313,7 +345,7 @@ public static Duration multiply(final long scalar, final Duration unit) throws A * @return The integral number of times {@code divisor} goes into {@code dividend}. */ public static long divide(final Duration dividend, final Duration divisor) { - return dividend.durationInMicroseconds / divisor.durationInMicroseconds; + return dividend.micros / divisor.micros; } /** @@ -324,7 +356,7 @@ public static long divide(final Duration dividend, final Duration divisor) { * @return The largest duration that can fit into the {@code dividend} a {@code divisor} number of times. */ public static Duration divide(final Duration dividend, final long divisor) { - return new Duration(dividend.durationInMicroseconds / divisor); + return new Duration(dividend.micros / divisor); } /** @@ -335,7 +367,7 @@ public static Duration divide(final Duration dividend, final long divisor) { * @return The span of time left over. */ public static Duration remainder(final Duration dividend, final Duration divisor) { - return new Duration(dividend.durationInMicroseconds % divisor.durationInMicroseconds); + return new Duration(dividend.micros % divisor.micros); } /** @@ -348,10 +380,10 @@ public static Duration remainder(final Duration dividend, final Duration divisor public static double ratio(final Duration dividend, final Duration divisor) { // Avoid casting potentially very large quantities to double before division. // We handle the integral part separately before casting. - final long integralPart = dividend.durationInMicroseconds / divisor.durationInMicroseconds; + final long integralPart = dividend.micros / divisor.micros; final double fractionalPart = - ((double) (dividend.durationInMicroseconds % divisor.durationInMicroseconds)) - / ((double) divisor.durationInMicroseconds); + ((double) (dividend.micros % divisor.micros)) + / ((double) divisor.micros); return integralPart + fractionalPart; } @@ -514,7 +546,7 @@ public boolean noLongerThan(final Duration other) { * @see Duration#compareTo(Duration) */ public boolean isNegative() { - return this.durationInMicroseconds < 0; + return this.micros < 0; } /** @@ -523,7 +555,7 @@ public boolean isNegative() { * @see Duration#compareTo(Duration) */ public boolean isPositive() { - return this.durationInMicroseconds > 0; + return this.micros > 0; } /** @@ -533,25 +565,21 @@ public boolean isPositive() { */ public boolean isZero() { - return this.durationInMicroseconds == 0; + return this.micros == 0; } public boolean isEqualTo(final Duration other) { if(other == null) return false; - return this.durationInMicroseconds == other.durationInMicroseconds; + return this.micros == other.micros; } - /** Determine whether two durations are the same. */ - @Override - @Deprecated - public boolean equals(final Object o) { - if (!(o instanceof final Duration other)) return false; - return this.isEqualTo(other); + public static Duration parseISO8601(final String iso8601String) { + final var javaDuration = java.time.Duration.parse(iso8601String); + return microseconds(javaDuration.getSeconds() * 1000000L + javaDuration.getNano() / 1000L); } - @Override - public int hashCode() { - return Long.hashCode(this.durationInMicroseconds); + public String toISO8601() { + return java.time.Duration.of(micros, ChronoUnit.MICROS).toString(); } /** @@ -590,7 +618,7 @@ public String toString() { /** Determine whether this duration is greater than, less than, or equal to another duration. */ @Override public int compareTo(final Duration other) { - return Long.compare(this.durationInMicroseconds, other.durationInMicroseconds); + return Long.compare(this.micros, other.micros); } } diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt index 256b2d3d85..b3208bce63 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt @@ -1,6 +1,6 @@ package gov.nasa.jpl.aerie.procedural.scheduling.simulation -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration sealed interface CheckpointGeneration { data class FixedPeriod(val period: Duration): CheckpointGeneration diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt index d5c904cd3b..2772238e5e 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt @@ -1,6 +1,6 @@ package gov.nasa.jpl.aerie.procedural.scheduling.simulation -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration sealed interface CheckpointRetention { data object All: CheckpointRetention diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt index 8ea928608c..fa9a766744 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt @@ -1,6 +1,6 @@ package gov.nasa.jpl.aerie.procedural.scheduling.simulation -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration sealed interface PauseBehavior { data class AfterDuration(val dur: Duration): PauseBehavior diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformer.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformer.kt index a859128b59..c5debc7494 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformer.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformer.kt @@ -1,5 +1,7 @@ package gov.nasa.jpl.aerie.timeline +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration + /** A functional interface for transforming bounds for operations that transform intervals. */ fun interface BoundsTransformer { /** diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Duration.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Duration.kt deleted file mode 100644 index 438b1a1463..0000000000 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Duration.kt +++ /dev/null @@ -1,261 +0,0 @@ -package gov.nasa.jpl.aerie.timeline - -import java.time.Instant -import java.time.temporal.ChronoUnit -import kotlin.math.ceil -import kotlin.math.floor -import kotlin.math.round - -/** - * A relative duration of time, represented as a long integer of microseconds. - */ -data class Duration(private val micros: Long) : Comparable { - - /** Returns the negative of this duration. */ - fun negate() = Duration(-micros) - /** @see negate */ - operator fun unaryMinus() = negate() - - /** Adds another duration to this. */ - operator fun plus(other: Duration) = Duration(micros + other.micros) - - /** Adds this to an instant, to produce another instant. */ - operator fun plus(instant: Instant) = instant + this - - /** Add this and another duration, saturating at the maximum and minimum bounds. */ - infix fun saturatingPlus(other: Duration) = Duration(saturatingAddInternal(micros, other.micros)) - - /** Subtracts another duration from this. */ - operator fun minus(other: Duration) = Duration(micros - other.micros) - - /** Subtract another duration from this, saturating at the maximum and minimum bounds. */ - infix fun saturatingMinus(other: Duration) = saturatingPlus(-other) - - /** Multiplies this by a long scalar. */ - operator fun times(scalar: Long) = Duration(micros * scalar) - /** Multiplies this by a double scalar. */ - operator fun times(scalar: Double) = roundNearest(scalar, this) - - /** - * Divides this by a duration divisor to produce a long, rounded down. - * - * To calculate this division as a double, use [ratioOver]. - */ - operator fun div(divisor: Duration) = micros / divisor.micros - /** Divides this by a long divisor. */ - operator fun div(divisor: Long) = Duration(micros / divisor) - /** Divides this by a double divisor. */ - operator fun div(divisor: Double) = roundNearest(1 / divisor, this) - - /** Remainder division between this and another duration. */ - operator fun rem(divisor: Duration) = Duration(micros % divisor.micros) - - /** Computes the ratio of this over another duration, as a double. */ - infix fun ratioOver(unit: Duration): Double { - val integralPart = micros / unit.micros - val fractionalPart = ((micros % unit.micros).toDouble() - / unit.micros.toDouble()) - return integralPart + fractionalPart - } - - /** Whether this is shorter than another duration. */ - infix fun shorterThan(other: Duration) = this < other - - /** Whether this is shorter than or equal to another duration. */ - infix fun shorterThanOrEqualTo(other: Duration) = this <= other - - /** Whether this is longer than another duration. */ - infix fun longerThan(other: Duration) = this > other - - /** Whether this is longer than or equal to another duration. */ - infix fun longerThanOrEqualTo(other: Duration) = this >= other - - /** Creates an interval from this to another duration, inclusive. */ - operator fun rangeTo(other: Duration) = Interval.between(this, other) - - /** Creates an interval from this to another duration, including the start but excluding the end. */ - operator fun rangeUntil(other: Duration) = Interval.betweenClosedOpen(this, other) - - /** Whether this duration is negative. */ - fun isNegative() = micros < 0 - - /** Whether this duration is positive. */ - fun isPositive() = micros > 0 - - /** Whether this duration is equal to zero. */ - fun isZero() = micros == 0L - - /** Converts this duration to an ISO 8601 duration string. */ - fun toISO8601() = java.time.Duration.of(micros, ChronoUnit.MICROS).toString() - - /** - * Obtain a human-readable representation of this duration. - * - * For example, `duration(-5, MINUTES).plus(1, SECOND).plus(2, MICROSECONDS)` is rendered as "-00:04:58.999998". - */ - override fun toString(): String { - var rest = this - val sign = if (isNegative()) "-" else "+" - val hours: Long - if (isNegative()) { - hours = -(rest / HOUR) - rest = -(rest % HOUR) - } else { - hours = rest / HOUR - rest %= HOUR - } - val minutes = rest / MINUTE - rest %= MINUTE - val seconds = rest / SECOND - rest %= SECOND - val microseconds = rest / MICROSECOND - return String.format("%s%02d:%02d:%02d.%06d", sign, hours, minutes, seconds, microseconds) - } - - /** Determine whether this duration is greater than, less than, or equal to another duration. */ - override fun compareTo(other: Duration) = micros.compareTo(other.micros) - - /***/ companion object { - /** - * The smallest observable span of time between instants. - * - * This quantity can be used to obtain the smallest representable deviation from a given instant, i.e. - * `instant.minus(EPSILON)` and `instant.plus(EPSILON)`. The precise deviation from `instant` should not be assumed, - * except that it is no larger than [Duration.MICROSECOND]. - * - */ - @JvmField val EPSILON = Duration(1) - - /** - * The empty span of time. - */ - @JvmField val ZERO = Duration(0) - - private const val MICROSECONDS_MIN = Long.MIN_VALUE / 2 - private const val MICROSECONDS_MAX = Long.MAX_VALUE / 2 - - /** - * The largest valid negative span of time. Attempting to go "more negative" may cause an exception. - * - * The value of this quantity should not be assumed. Currently, it equals half of long-min microseconds. - * This is for two reasons: - * 1. It may avoid some overflow errors by giving a 2x margin. - * 2. Java durations are serialized to ISO8601 duration format using only hours, minutes, and fractional seconds. - * This is to avoid timekeeping issues related to leap seconds and leap years. Postgres intervals can represent - * durations up to hundreds of millions of years, but only by using days and months. Thus, attempting to - * interact with Postgres using a duration of long-min or long-max microseconds, represented as hours, causes an overflow. - * However, long-min / 2 and long-max / 2 microseconds are small enough to not cause overflow. - */ - @JvmField val MIN_VALUE = Duration(MICROSECONDS_MIN) - - /** - * The largest valid positive span of time. Attempting to go "more positive" may cause an exception. - * - * The value of this quantity should not be assumed. Currently, it equals half of long-max microseconds. - * - * @see MIN_VALUE for an explanation of the choice of extremes. - */ - @JvmField val MAX_VALUE = Duration(MICROSECONDS_MAX) - - /** One microsecond (μs). */ - @JvmField val MICROSECOND = Duration(1) - - /** One millisecond (ms), equal to 1000μs. */ - @JvmField val MILLISECOND = MICROSECOND * 1000 - - /** One second (s), equal to 1000ms. */ - @JvmField val SECOND = MILLISECOND * 1000 - - /** One minute (m), equal to 60s. */ - @JvmField val MINUTE = SECOND * 60 - - /** One hour (h), equal to 60m. */ - @JvmField val HOUR = MINUTE * 60 - - /** One hour (d), equal to 24h. */ - @JvmField val DAY = HOUR * 24 - - /** Constructs a duration with a given number of microseconds. */ - @JvmStatic fun microseconds(quantity: Long) = Duration(quantity) - - /** Constructs a duration with a given number of milliseconds. */ - @JvmStatic fun milliseconds(quantity: Long) = MILLISECOND * quantity - - /** Constructs a duration with a given number of seconds. */ - @JvmStatic fun seconds(quantity: Long) = SECOND * quantity - - /** Constructs a duration with a given number of minutes. */ - @JvmStatic fun minutes(quantity: Long) = MINUTE * quantity - - /** Constructs a duration with a given number of hours. */ - @JvmStatic fun hours(quantity: Long) = HOUR * quantity - - /** Constructs a duration with a given number of days. */ - @JvmStatic fun days(quantity: Long) = DAY * quantity - - /** - * Construct a duration in terms of a real quantity of some unit, - * rounding to the nearest representable value above. - */ - @JvmStatic fun roundUpward(quantity: Double, unit: Duration): Duration { - val integerPart = floor(quantity) - val decimalPart = quantity - integerPart - return unit * (integerPart.toLong()) + EPSILON.times(ceil(decimalPart * (unit / EPSILON)).toLong()) - } - - /** - * Construct a duration in terms of a real quantity of some unit, - * rounding to the nearest representable value below. - */ - @JvmStatic fun roundDownward(quantity: Double, unit: Duration): Duration { - val integerPart = floor(quantity) - val decimalPart = quantity - integerPart - return unit * (integerPart.toLong()) + - EPSILON * (floor(decimalPart * (unit / EPSILON)).toLong()) - } - - /** - * Construct a duration in terms of a real quantity of some unit, - * rounding to the nearest representable value. - */ - @JvmStatic fun roundNearest(quantity: Double, unit: Duration): Duration { - val integerPart = floor(quantity) - val decimalPart = quantity - integerPart - return unit * (integerPart.toLong()) + EPSILON * (round(decimalPart * (unit / EPSILON)).toLong()) - } - - private fun saturatingAddInternal(left: Long, right: Long): Long { - val result = left + right - return if (result xor left and (result xor right) < 0) { - val saturatedToLongBounds = Long.MIN_VALUE - (result ushr java.lang.Long.SIZE - 1) - - // This block is added to clamp the value to less than the long data type bounds. - // IntelliJ says that the second if-branch is always true, but that's some black - // magic wizardry, so I'm not touching it. The compiler can make that optimization - // if its really that smart. - if (saturatedToLongBounds < MICROSECONDS_MIN) Long.MIN_VALUE / 2 - else if (saturatedToLongBounds > MICROSECONDS_MAX) Long.MAX_VALUE / 2 - else saturatedToLongBounds - } else result - } - - /** Finds the minimum duration among the arguments. */ - @JvmStatic fun min(vararg durations: Duration) = durations.min() - - /** Finds the minimum duration among the arguments. */ - @JvmStatic fun max(vararg durations: Duration) = durations.max() - - /** Adds a duration to an instant, to produce another instant. */ - @JvmStatic operator fun Instant.plus(duration: Duration): Instant = plusMillis(duration / MILLISECOND) - .plusNanos(1000 * ((duration % MILLISECOND).micros)) - - /** Subtracts a duration from an instant, to produce another instant. */ - @JvmStatic operator fun Instant.minus(other: Instant) = Duration(other.until(this, ChronoUnit.MICROS)) - - /** Parses a duration from a ISO 8601 formatted duration string. */ - @JvmStatic fun parseISO8601(iso8601String: String?): Duration { - val javaDuration = java.time.Duration.parse(iso8601String) - return microseconds(javaDuration.seconds * 1000000L + javaDuration.nano / 1000L) - } - } -} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Interval.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Interval.kt index 01eb2c412e..c8c8c10e0e 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Interval.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Interval.kt @@ -1,5 +1,7 @@ package gov.nasa.jpl.aerie.timeline +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike /** diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Booleans.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Booleans.kt index f8f48b777f..fbf569536c 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Booleans.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Booleans.kt @@ -1,7 +1,9 @@ package gov.nasa.jpl.aerie.timeline.collections.profiles +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.timeline.* +import gov.nasa.jpl.aerie.timeline.util.duration.unaryMinus import gov.nasa.jpl.aerie.timeline.ops.BooleanOps import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.ops.SerialConstantOps @@ -65,8 +67,8 @@ data class Booleans(private val timeline: Timeline, Booleans>): unsafeMapIntervals( { i -> Interval.between( - Duration.min(i.start.saturatingMinus(shiftRising), i.start.saturatingMinus(shiftFalling)), - Duration.max(i.end.saturatingMinus(shiftRising), i.end.saturatingMinus(shiftFalling)), + Duration.min(Duration.saturatingAdd(i.start, -shiftRising), Duration.saturatingAdd(i.start, -shiftFalling)), + Duration.max(Duration.saturatingAdd(i.end, -shiftRising), Duration.saturatingAdd(i.end, -shiftFalling)), i.startInclusivity, i.endInclusivity ) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Numbers.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Numbers.kt index b0733115d6..c8b2a2e225 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Numbers.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Numbers.kt @@ -1,11 +1,10 @@ package gov.nasa.jpl.aerie.timeline.collections.profiles +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.timeline.BaseTimeline -import gov.nasa.jpl.aerie.timeline.Duration -import gov.nasa.jpl.aerie.timeline.Interval +import gov.nasa.jpl.aerie.timeline.* +import gov.nasa.jpl.aerie.timeline.util.duration.unaryMinus import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.Timeline import gov.nasa.jpl.aerie.timeline.ops.SerialConstantOps import gov.nasa.jpl.aerie.timeline.ops.numeric.PrimitiveNumberOps import gov.nasa.jpl.aerie.timeline.ops.numeric.SerialNumericOps @@ -201,7 +200,7 @@ data class Numbers(private val timeline: Timeline, Numbers // This unchecked cast is OK because the difference between two primitives of different type // will never be a third type. @Suppress("UNCHECKED_CAST") - override fun shiftedDifference(range: Duration) = shift(range.negate()).minus(this) as Numbers + override fun shiftedDifference(range: Duration) = (shift(-range) - this) as Numbers /***/ companion object { /** diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Real.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Real.kt index 3311c7cb8b..630c9b506f 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Real.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Real.kt @@ -1,5 +1,6 @@ package gov.nasa.jpl.aerie.timeline.collections.profiles +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.timeline.* import gov.nasa.jpl.aerie.timeline.ops.numeric.LinearOps @@ -9,6 +10,7 @@ import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation import gov.nasa.jpl.aerie.timeline.payloads.transpose import gov.nasa.jpl.aerie.timeline.util.preprocessList import gov.nasa.jpl.aerie.timeline.util.truncateList +import gov.nasa.jpl.aerie.timeline.util.duration.unaryMinus import kotlin.jvm.optionals.getOrNull import kotlin.math.pow @@ -173,7 +175,7 @@ data class Real(private val timeline: Timeline, Real>): { l, r, i -> l.valueAt(i.start) == from && r.valueAt(i.start) == to } )) - override fun shiftedDifference(range: Duration) = shift(range.negate()).minus(this) + override fun shiftedDifference(range: Duration) = shift(-range).minus(this) /** * An exception for linear profile operations; usually thrown in contexts that diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/BooleanOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/BooleanOps.kt index e71ffa4bba..a7575dc92d 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/BooleanOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/BooleanOps.kt @@ -1,8 +1,9 @@ package gov.nasa.jpl.aerie.timeline.ops -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.Interval import gov.nasa.jpl.aerie.timeline.collections.Intervals +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo /** * Operations mixin for timelines of booleans. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt index 1928dc3ed5..a3eb04c420 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt @@ -1,5 +1,6 @@ package gov.nasa.jpl.aerie.timeline.ops +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.* import gov.nasa.jpl.aerie.timeline.collections.Intervals import gov.nasa.jpl.aerie.timeline.collections.Windows diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/NonZeroDurationOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/NonZeroDurationOps.kt index 8a0e029c17..33ccdaca71 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/NonZeroDurationOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/NonZeroDurationOps.kt @@ -1,9 +1,10 @@ package gov.nasa.jpl.aerie.timeline.ops import gov.nasa.jpl.aerie.timeline.CollectOptions -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.Interval import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive +import gov.nasa.jpl.aerie.timeline.util.duration.div import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike import gov.nasa.jpl.aerie.timeline.util.truncateList diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/ParallelOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/ParallelOps.kt index 381f5b2a07..25092db8ef 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/ParallelOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/ParallelOps.kt @@ -1,5 +1,6 @@ package gov.nasa.jpl.aerie.timeline.ops +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.* import gov.nasa.jpl.aerie.timeline.collections.Intervals import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers @@ -10,6 +11,7 @@ import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.util.map2SegmentLists import gov.nasa.jpl.aerie.timeline.util.truncateList +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo /** * Operations mixin for timelines of potentially overlapping objects. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOps.kt index 390924f070..e77374f17b 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOps.kt @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.timeline.ops.numeric +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.BoundsTransformer -import gov.nasa.jpl.aerie.timeline.Duration import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation @@ -32,7 +32,7 @@ interface LinearOps>: NumericOps { */ fun rate(unit: Duration = Duration.SECOND) = if (unit == Duration.SECOND) mapValues(::Numbers) { it.value.rate } - else mapValues(::Numbers) { it.value.rate / (Duration.SECOND ratioOver unit) } + else mapValues(::Numbers) { it.value.rate / (Duration.SECOND.ratioOver(unit)) } override fun shift(dur: Duration) = unsafeMap(BoundsTransformer.shift(dur), false) { v -> Segment(v.interval.shiftBy(dur), LinearEquation(v.value.initialTime.plus(dur), v.value.initialValue, v.value.rate)) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOps.kt index 853c350142..e5488433a5 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOps.kt @@ -1,6 +1,6 @@ package gov.nasa.jpl.aerie.timeline.ops.numeric -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.collections.profiles.Real diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/LinearEquation.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/LinearEquation.kt index d123e398e4..21a55ba243 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/LinearEquation.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/LinearEquation.kt @@ -1,11 +1,12 @@ package gov.nasa.jpl.aerie.timeline.payloads -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.Interval import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive import gov.nasa.jpl.aerie.timeline.collections.profiles.Real import gov.nasa.jpl.aerie.timeline.collections.profiles.Booleans +import gov.nasa.jpl.aerie.timeline.util.duration.div import java.util.function.BiFunction import kotlin.math.abs import kotlin.math.absoluteValue diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Activity.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Activity.kt index 69a4a85868..1a54cfc946 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Activity.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Activity.kt @@ -1,6 +1,6 @@ package gov.nasa.jpl.aerie.timeline.payloads.activities -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike /** Unifying interface for activity instances and directives. */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt index 4d93d8389f..486e6246e4 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt @@ -1,6 +1,6 @@ package gov.nasa.jpl.aerie.timeline.payloads.activities -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.Interval /** A wrapper of any type of activity directive containing common data. */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Instance.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Instance.kt index 2af9a5e865..59ec8c298c 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Instance.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Instance.kt @@ -1,6 +1,6 @@ package gov.nasa.jpl.aerie.timeline.payloads.activities -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.Interval /** A wrapper of any type of activity instance containing common data. */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresSimulationResults.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresSimulationResults.kt index b1222a01d4..ea89145981 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresSimulationResults.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresSimulationResults.kt @@ -2,7 +2,7 @@ package gov.nasa.jpl.aerie.timeline.plan import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/Plan.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/Plan.kt index fa0034a9a3..d6e5ec17a3 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/Plan.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/Plan.kt @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.timeline.plan import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.timeline.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.Interval import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective import gov.nasa.jpl.aerie.timeline.collections.Directives diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulatedPlan.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulatedPlan.kt index 7c27aa4132..2b251428a9 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulatedPlan.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulatedPlan.kt @@ -1,19 +1,5 @@ package gov.nasa.jpl.aerie.timeline.plan -import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser -import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.timeline.Duration -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.* -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.payloads.activities.Instance -import gov.nasa.jpl.aerie.timeline.collections.Instances -import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegmentsOp -import java.io.StringReader -import java.sql.PreparedStatement -import javax.json.Json -import kotlin.jvm.optionals.getOrNull - /** A connection to Aerie's database for a particular simulation result. */ data class SimulatedPlan( private val plan: Plan, diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/duration/Duration.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/duration/Duration.kt new file mode 100644 index 0000000000..01709734ca --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/duration/Duration.kt @@ -0,0 +1,37 @@ +package gov.nasa.jpl.aerie.timeline.util.duration + +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.* +import gov.nasa.jpl.aerie.timeline.Interval +import java.time.Instant +import java.time.temporal.ChronoUnit + +/** Negation operator for durations. */ +operator fun Duration.unaryMinus(): Duration = this.negate() + +/** Create an interval between two durations, including both endpoints, using the `..` operator. */ +operator fun Duration.rangeTo(other: Duration): Interval = Interval.between(this, other) + +/** Create an interval between two durations, including the start and excluding the end, using the `..<` operator. */ +operator fun Duration.rangeUntil(other: Duration): Interval = Interval.betweenClosedOpen(this, other) + +/** + * Divides this by a duration divisor to produce a long, rounded down. + * + * To calculate this division as a double, use [ratioOver]. + */ +operator fun Duration.div(divisor: Duration): Long = this.dividedBy(divisor) +/** Divides this by a long divisor. */ +operator fun Duration.div(divisor: Long): Duration = this.dividedBy(divisor) +/** Divides this by a double divisor. */ +operator fun Duration.div(divisor: Double): Duration = roundNearest(1 / divisor, this) + +/** Remainder division between this and another duration. */ +operator fun Duration.rem(divisor: Duration): Duration = this.remainderOf(divisor) + +/** Adds a duration to an instant, to produce another instant. */ +operator fun Instant.plus(duration: Duration): Instant = plusMillis(duration / MILLISECOND) + .plusNanos(1000 * ((duration % MILLISECOND) / MICROSECOND)) + +/** Subtracts a duration from an instant, to produce another instant. */ +operator fun Instant.minus(other: Instant): Duration = microseconds(other.until(this, ChronoUnit.MICROS)) diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformerTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformerTest.kt index edac277d6c..1a8c158b5a 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformerTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformerTest.kt @@ -1,6 +1,6 @@ package gov.nasa.jpl.aerie.timeline -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/IntervalTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/IntervalTest.kt index 3eb8ecb4df..3184c7d4d8 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/IntervalTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/IntervalTest.kt @@ -1,11 +1,14 @@ package gov.nasa.jpl.aerie.timeline -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.EMPTY import gov.nasa.jpl.aerie.timeline.Interval.Companion.at import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Companion.betweenClosedOpen import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.* +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.jpl.aerie.timeline.util.duration.rangeUntil import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/SegmentTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/SegmentTest.kt index 30c5a780cb..db19e54d6a 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/SegmentTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/SegmentTest.kt @@ -1,6 +1,6 @@ package gov.nasa.jpl.aerie.timeline -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.at import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.payloads.transpose diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/WindowsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/WindowsTest.kt index ccec6dd57f..1bbbf582ff 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/WindowsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/WindowsTest.kt @@ -1,12 +1,14 @@ package gov.nasa.jpl.aerie.timeline.collections import gov.nasa.jpl.aerie.timeline.CollectOptions -import gov.nasa.jpl.aerie.timeline.Duration -import gov.nasa.jpl.aerie.timeline.Duration.Companion.milliseconds -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.milliseconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.at import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.jpl.aerie.timeline.util.duration.rangeUntil import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/BooleansTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/BooleansTest.kt index fdf23be27d..baf4802982 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/BooleansTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/BooleansTest.kt @@ -1,9 +1,11 @@ package gov.nasa.jpl.aerie.timeline.collections.profiles +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.CollectOptions -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.payloads.Segment +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.jpl.aerie.timeline.util.duration.rangeUntil import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/NumbersTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/NumbersTest.kt index b799d0c996..2e67a83cad 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/NumbersTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/NumbersTest.kt @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.timeline.collections.profiles -import gov.nasa.jpl.aerie.timeline.Duration -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.Interval import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.payloads.Segment diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/RealTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/RealTest.kt index 8ba121c39f..0653a7e9fe 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/RealTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/RealTest.kt @@ -1,9 +1,10 @@ package gov.nasa.jpl.aerie.timeline.collections.profiles -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation import gov.nasa.jpl.aerie.timeline.payloads.Segment import org.junit.jupiter.api.Assertions.assertIterableEquals +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo import org.junit.jupiter.api.Test class RealTest { diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOpsTest.kt index a06ca60b9f..936315a2b4 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOpsTest.kt @@ -1,8 +1,9 @@ package gov.nasa.jpl.aerie.timeline.ops +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.milliseconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.* -import gov.nasa.jpl.aerie.timeline.Duration.Companion.milliseconds -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.at import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Companion.betweenClosedOpen @@ -13,6 +14,8 @@ import gov.nasa.jpl.aerie.timeline.collections.profiles.Constants import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers import gov.nasa.jpl.aerie.timeline.collections.profiles.Booleans import gov.nasa.jpl.aerie.timeline.payloads.Segment +import gov.nasa.jpl.aerie.timeline.util.duration.div +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/NonZeroDurationOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/NonZeroDurationOpsTest.kt index a1ff62a826..e8d95fe3d3 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/NonZeroDurationOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/NonZeroDurationOpsTest.kt @@ -1,12 +1,13 @@ package gov.nasa.jpl.aerie.timeline.ops +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.CollectOptions -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/ParallelOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/ParallelOpsTest.kt index 73309f16fb..8ac29a84f3 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/ParallelOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/ParallelOpsTest.kt @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.timeline.ops +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.NullBinaryOperation -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.at import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Companion.betweenClosedOpen @@ -10,6 +10,7 @@ import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.collections.Intervals import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialConstantTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialConstantTest.kt index d9834f3bfe..6983c4961a 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialConstantTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialConstantTest.kt @@ -1,12 +1,13 @@ package gov.nasa.jpl.aerie.timeline.ops -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.at import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialSegmentOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialSegmentOpsTest.kt index 2ebb511132..4e8c86376a 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialSegmentOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialSegmentOpsTest.kt @@ -1,8 +1,8 @@ package gov.nasa.jpl.aerie.timeline.ops +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.milliseconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.NullBinaryOperation -import gov.nasa.jpl.aerie.timeline.Duration.Companion.milliseconds -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.at import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Companion.betweenClosedOpen @@ -10,6 +10,7 @@ import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.collections.profiles.Constants +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOpsTest.kt index ccc7a46ef0..0920366bcb 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOpsTest.kt @@ -1,9 +1,10 @@ package gov.nasa.jpl.aerie.timeline.ops.numeric -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.collections.profiles.Real import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation import gov.nasa.jpl.aerie.timeline.payloads.Segment +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/PrimitiveNumberOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/PrimitiveNumberOpsTest.kt index 75d6e140eb..1ccc20d84f 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/PrimitiveNumberOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/PrimitiveNumberOpsTest.kt @@ -1,6 +1,6 @@ package gov.nasa.jpl.aerie.timeline.ops.numeric -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.at import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOpsTest.kt index fd4629397b..92f0a4bb9f 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOpsTest.kt @@ -1,12 +1,14 @@ package gov.nasa.jpl.aerie.timeline.ops.numeric -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.Interval import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers import gov.nasa.jpl.aerie.timeline.collections.profiles.Real import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation import gov.nasa.jpl.aerie.timeline.payloads.Segment +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.jpl.aerie.timeline.util.duration.rangeUntil import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/CoalesceTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/CoalesceTest.kt index 725b015903..551da292c8 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/CoalesceTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/CoalesceTest.kt @@ -1,10 +1,11 @@ package gov.nasa.jpl.aerie.timeline.util -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.at import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.* import gov.nasa.jpl.aerie.timeline.payloads.Segment +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/LinearEquationTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/LinearEquationTest.kt index 72c5c55a5b..0956734f82 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/LinearEquationTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/LinearEquationTest.kt @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.timeline.util -import gov.nasa.jpl.aerie.timeline.Duration -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Companion.betweenClosedOpen import gov.nasa.jpl.aerie.timeline.payloads.Segment diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/ListCollectorTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/ListCollectorTest.kt index a4702debaa..baa1e8ee08 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/ListCollectorTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/ListCollectorTest.kt @@ -1,11 +1,12 @@ package gov.nasa.jpl.aerie.timeline.util +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.CollectOptions -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.collections.Intervals +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo val bounds = (seconds(0) .. seconds(10)) diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2Test.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2Test.kt index 27fd97201c..5810ebbcf8 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2Test.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2Test.kt @@ -1,7 +1,8 @@ package gov.nasa.jpl.aerie.timeline.util +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.NullBinaryOperation -import gov.nasa.jpl.aerie.timeline.Duration.Companion.seconds +import gov.nasa.jpl.aerie.timeline.durationUtils.* import gov.nasa.jpl.aerie.timeline.Interval import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.* From fabbcf3bdd09495cf338d45e06f2d4b21d05388a Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 25 Apr 2024 17:42:53 -0700 Subject: [PATCH 005/108] Fix inspect function --- .../kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt index a3eb04c420..3f4ff9eee5 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt @@ -10,6 +10,7 @@ import gov.nasa.jpl.aerie.timeline.util.coalesceList import gov.nasa.jpl.aerie.timeline.util.map2ParallelLists import gov.nasa.jpl.aerie.timeline.util.sorted import gov.nasa.jpl.aerie.timeline.util.truncateList +import java.util.function.Consumer /** * General operations mixin for all timeline types. @@ -72,9 +73,9 @@ interface GeneralOps, THIS: GeneralOps>: Timeline) -> Unit) = BaseTimeline(ctor) { + fun inspect(f: Consumer>) = BaseTimeline(ctor) { val list = collect(it) - f(list) + f.accept(list) list }.specialize() From 0dfbd8af7f7b78ef706beb45c0ebb2ddd9be8b07 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 9 May 2024 15:11:19 -0700 Subject: [PATCH 006/108] Documentation updates --- procedural/scheduling/MODULE_DOCS.md | 20 ++++++++ procedural/scheduling/build.gradle | 1 - .../aerie/procedural/scheduling/Procedure.kt | 1 + .../aerie/procedural/scheduling/plan/Edit.kt | 8 +++- .../scheduling/plan/EditablePlan.kt | 23 +++++++++ .../scheduling/plan/NewDirective.kt | 19 +++++++- .../simulation/CheckpointGeneration.kt | 14 ++++-- .../simulation/CheckpointRetention.kt | 11 ++++- .../scheduling/simulation/PauseBehavior.kt | 19 ++++++-- .../scheduling/simulation/SimulateOptions.kt | 4 ++ .../timeline/payloads/activities/Anchor.kt | 33 ------------- .../payloads/activities/AnyDirective.kt | 1 + .../timeline/payloads/activities/Directive.kt | 6 +-- .../payloads/activities/DirectiveStart.kt | 47 +++++++++++++++++++ .../aerie/timeline/plan/AeriePostgresPlan.kt | 5 +- .../aerie/timeline/plan/SimulationResults.kt | 1 + .../nasa/jpl/aerie/timeline/plan/SqlUtils.kt | 4 +- .../nasa/jpl/aerie/timeline/util/Map2Test.kt | 3 +- 18 files changed, 168 insertions(+), 52 deletions(-) delete mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Anchor.kt create mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/DirectiveStart.kt diff --git a/procedural/scheduling/MODULE_DOCS.md b/procedural/scheduling/MODULE_DOCS.md index 6caed014a6..9decc70e6f 100644 --- a/procedural/scheduling/MODULE_DOCS.md +++ b/procedural/scheduling/MODULE_DOCS.md @@ -1 +1,21 @@ # Module Scheduling + +This library provides tools for creating scheduling procedures, based on the timeline library. +To write a procedure, create a class that implements the Procedure interface. +Then, follow the tutorial documentation (TODO: link to tutorial documentation once written) +to package and upload your constraint to Aerie. + +# Package gov.nasa.jpl.aerie.procedural.scheduling +The top-level procedure types. + +# Package gov.nasa.jpl.aerie.procedural.scheduling.plan +Contains the EditablePlan interface that procedure execution environments need to +implement. + +# Package gov.nasa.jpl.aerie.procedural.scheduling.simulation +Contains configuration options for spawning simulations. + +# Package gov.nasa.jpl.aerie.procedural.scheduling.annotations +Annotations for serializing and deserializing procedure arguments. + + diff --git a/procedural/scheduling/build.gradle b/procedural/scheduling/build.gradle index 1a7118bf8a..1a76137edc 100644 --- a/procedural/scheduling/build.gradle +++ b/procedural/scheduling/build.gradle @@ -43,7 +43,6 @@ dokkaHtmlPartial.configure { moduleName.set("Scheduling") reportUndocumented.set(true) - failOnWarning.set(true) // contains descriptions for the module and the packages includes.from("MODULE_DOCS.md") diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Procedure.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Procedure.kt index 9ccdce849a..ae49516ea8 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Procedure.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Procedure.kt @@ -3,6 +3,7 @@ package gov.nasa.jpl.aerie.procedural.scheduling import gov.nasa.jpl.aerie.procedural.scheduling.plan.EditablePlan import gov.nasa.jpl.aerie.timeline.CollectOptions +/** The interface that all scheduling procedures must satisfy. */ interface Procedure { /** * Run the procedure. diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt index 431142d3a1..a9c93a65ea 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt @@ -3,6 +3,12 @@ package gov.nasa.jpl.aerie.procedural.scheduling.plan import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive +/** + * Edits that can be made to the plan. + * + * Currently only creating new activities is supported. + */ sealed interface Edit { - data class Create(val directive: Directive): Edit + /** Create a new activity from a given directive. */ + data class Create(/***/ val directive: Directive): Edit } diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt index 280059a89b..eb7531f67b 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt @@ -4,13 +4,36 @@ import gov.nasa.jpl.aerie.procedural.scheduling.simulation.SimulateOptions import gov.nasa.jpl.aerie.timeline.plan.Plan import gov.nasa.jpl.aerie.timeline.plan.SimulationResults +/** A plan representation that can be edited and simulated. */ interface EditablePlan: Plan { + /** Get the latest simulation results. */ fun latestResults(): SimulationResults? + /** + * Create a new activity. + * + * @param directive a directive without a directive id. + * @return a long, the directive id this activity will have. + */ fun create(directive: NewDirective): Long + + /** Commit plan edits, making them final. */ fun commit() + + /** + * Roll back uncommitted edits. + * + * @return the list of rolled back edits. + */ fun rollback(): List + /** + * Simulate the current plan, including committed and uncommitted changes. + * + * @param options configurations for the simulation. + */ fun simulate(options: SimulateOptions): SimulationResults + + /** A simplified version of [simulate] which uses the default configuration. */ fun simulate() = simulate(SimulateOptions()) } diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt index 8d5ae150e5..09f0df7b22 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt @@ -4,16 +4,33 @@ import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive import gov.nasa.jpl.aerie.timeline.payloads.activities.DirectiveStart +/** A new directive to be created, which doesn't have an id yet. */ data class NewDirective( + /** The activity's arguments. */ val inner: AnyDirective, + + /** The name of the activity. */ val name: String, + + /** The activity type. */ val type: String, + + /** The activity's start behavior. */ val start: DirectiveStart ) { + /** + * Resolves this activity into a proper [Directive] object. + * + * Users likely don't need to use this function; it is more for implementations of + * [EditablePlan]. + * + * @param id The id for the new directive. + * @param parent The activity this activity is anchored to, if applicable. + */ fun resolve(id: Long, parent: Directive<*>?): Directive { if (start is DirectiveStart.Anchor) { if (parent == null) throw IllegalArgumentException("Parent must provided when anchor is not null") - start.updateEstimate(parent.startTime + start.offset) + start.estimatedStart = parent.startTime + start.offset } return Directive( inner, diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt index b3208bce63..52e95e8b38 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt @@ -2,11 +2,19 @@ package gov.nasa.jpl.aerie.procedural.scheduling.simulation import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +/** Behavior for generating new checkpoints in simulation. */ sealed interface CheckpointGeneration { - data class FixedPeriod(val period: Duration): CheckpointGeneration - data class AtTimes(val times: List): CheckpointGeneration { - constructor(vararg times: Duration): this(times.asList()) + /** Generate a checkpoint at multiples of a regular period. */ + data class Periodic(/***/ val period: Duration): CheckpointGeneration + + /** Generate a checkpoint at a list of specific times. */ + data class AtTimes(/***/ val times: List): CheckpointGeneration { + /***/ constructor(/***/ vararg times: Duration): this(times.asList()) } + + /** Generate a checkpoint at the end of the simulation. */ data object AtEnd: CheckpointGeneration + + /** Do not generate any checkpoints. */ data object None: CheckpointGeneration } diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt index 2772238e5e..10ce6a4d13 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt @@ -2,8 +2,17 @@ package gov.nasa.jpl.aerie.procedural.scheduling.simulation import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +/** + * Behavior for retaining generated checkpoints in memory. + * + * Checkpoints can be very large; for large models maybe only a few can fit + * in memory at a time. + */ sealed interface CheckpointRetention { + /** Retain all checkpoints. */ data object All: CheckpointRetention + /** Retain only the latest checkpoint. Replace it with each new checkpoint. */ data object Latest: CheckpointRetention - data class DurationFromPresent(val dur: Duration): CheckpointRetention + /** Retain all checkpoints in within a range from the current simulation time. */ + data class DurationFromPresent(/***/ val dur: Duration): CheckpointRetention } diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt index fa9a766744..dcea98cafd 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt @@ -2,14 +2,25 @@ package gov.nasa.jpl.aerie.procedural.scheduling.simulation import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +/** Behavior for when the simulation should pause. */ sealed interface PauseBehavior { - data class AfterDuration(val dur: Duration): PauseBehavior - data class AfterActivity(val directive: Long): PauseBehavior + /** Pause after a given amount of time has elapsed. */ + data class AfterDuration(/***/ val dur: Duration): PauseBehavior + + /** Pause after a specific activity directive has finished. */ + data class AfterActivity(/** Id of the directive to wait for. */ val directive: Long): PauseBehavior + + /** Pause after all edits that were not included in the previous sim are simulated. */ data object AfterNewEdits: PauseBehavior + + /** Do not pause; continue to the end of the plan. */ data object AtEnd: PauseBehavior - data class EarliestOf(val pausePoints: List): PauseBehavior - data class LatestOf(val pausePoints: List): PauseBehavior + /** Pause at the earliest of a list of possible pause points. */ + data class EarliestOf(/***/ val pausePoints: List): PauseBehavior + + /** Pause at the latest of a list of possible pause points. */ + data class LatestOf(/***/ val pausePoints: List): PauseBehavior // very hard! // data class OnCondition(val condition: ???): PauseBehavior diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/SimulateOptions.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/SimulateOptions.kt index 26e8638b5a..033ae0d245 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/SimulateOptions.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/SimulateOptions.kt @@ -1,7 +1,11 @@ package gov.nasa.jpl.aerie.procedural.scheduling.simulation +/** Configuration for simulation. */ /* data */ class SimulateOptions( + // The following two options will be uncommented when checkpoint simulation is released. // val checkPointGeneration: CheckpointGeneration = CheckpointGeneration.None, // val checkpointRetention: CheckpointRetention = CheckpointRetention.All, + + // TODO This option will be uncommented before MVP release. (see `feat/procedural-scheduling` branch) // val pause: PauseBehavior = PauseBehavior.AtEnd, ) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Anchor.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Anchor.kt deleted file mode 100644 index 2d2273e1a6..0000000000 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Anchor.kt +++ /dev/null @@ -1,33 +0,0 @@ -package gov.nasa.jpl.aerie.timeline.payloads.activities - -import gov.nasa.jpl.aerie.merlin.protocol.types.Duration - -sealed interface DirectiveStart { - fun atNewTime(time: Duration): DirectiveStart - - data class Anchor( - val parentId: Long, - val offset: Duration, - val anchorPoint: AnchorPoint, - var estimatedStart: Duration - ): DirectiveStart { - enum class AnchorPoint { - Start, End; - - companion object { - fun anchorToStart(b: Boolean) = if (b) Start else End - } - } - - constructor(parentId: Long, offset: Duration, anchorPoint: AnchorPoint): this(parentId, offset, anchorPoint, Duration.ZERO) - - fun updateEstimate(d: Duration) { - estimatedStart = d - } - override fun atNewTime(time: Duration) = Anchor(parentId, offset + time - estimatedStart, anchorPoint, time) - } - - data class Absolute(val time: Duration): DirectiveStart { - override fun atNewTime(time: Duration) = Absolute(time) - } -} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyDirective.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyDirective.kt index 55c26650a8..bd62215846 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyDirective.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyDirective.kt @@ -7,6 +7,7 @@ import kotlin.jvm.optionals.getOrNull data class AnyDirective( /***/ @JvmField val arguments: Map ) { + /** Serialize this directive into a [SerializedValue]. */ fun serialize(): SerializedValue = SerializedValue.of(arguments) /***/ companion object { /** Converts a [SerializedValue] object containing activity arguments into an [AnyDirective] object. */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt index 486e6246e4..5f19675fa9 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt @@ -16,6 +16,7 @@ data class Directive( override val type: String, + /** The start behavior for this directive. */ val start: DirectiveStart, ): Activity> { override val startTime: Duration @@ -32,13 +33,12 @@ data class Directive( else throw Exception("Cannot change directive time to a non-instantaneous interval.") } - fun mapInner(f: (A) -> R) = Directive( + /** Transform the inner payload with a function, returning a new directive object. */ + fun mapInner(/***/ f: (A) -> R) = Directive( f(inner), name, id, type, start ) - - fun withNewAnchor(anchor: DirectiveStart.Anchor) = Directive(inner, name, id, type, anchor) } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/DirectiveStart.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/DirectiveStart.kt new file mode 100644 index 0000000000..5c04e150aa --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/DirectiveStart.kt @@ -0,0 +1,47 @@ +package gov.nasa.jpl.aerie.timeline.payloads.activities + +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration + +/** Start behavior for an activity directive. */ +sealed interface DirectiveStart { + /** Shift the activity so that it starts at the new time (may be approximate for anchored activities). */ + fun atNewTime(time: Duration): DirectiveStart + + /** For activities that start at a known, grounded time. */ + data class Absolute(/***/ val time: Duration): DirectiveStart { + override fun atNewTime(time: Duration) = Absolute(time) + } + + /** For activities that are anchored to another activity. */ + data class Anchor @JvmOverloads constructor( + /** Id of the parent it is anchored to. */ + val parentId: Long, + + /** Duration to offset from the parent (negative durations mean before the parent). */ + val offset: Duration, + + /** Which end of the parent to anchor to. */ + val anchorPoint: AnchorPoint, + + /** + * When the activity is estimated to start (approximate, and automatically set by [EditablePlan] implementations). + * + * Defaults to [Duration.ZERO] (plan start). + */ + var estimatedStart: Duration = Duration.ZERO + ): DirectiveStart { + + /** Which end of the parent to anchor to. */ + enum class AnchorPoint { + /***/ Start, + /***/ End; + + /***/ companion object { + /** Helper function; returns [Start] if `true`, [End] if `false`. */ + fun anchorToStart(b: Boolean) = if (b) Start else End + } + } + + override fun atNewTime(time: Duration) = Anchor(parentId, offset + time - estimatedStart, anchorPoint, time) + } +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresPlan.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresPlan.kt index 581c314621..d2258b6238 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresPlan.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresPlan.kt @@ -5,11 +5,12 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.timeline.BaseTimeline import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.BoundsTransformer -import gov.nasa.jpl.aerie.timeline.durationUtils.* import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive import gov.nasa.jpl.aerie.timeline.collections.Directives import gov.nasa.jpl.aerie.timeline.payloads.activities.DirectiveStart +import gov.nasa.jpl.aerie.timeline.util.duration.minus +import gov.nasa.jpl.aerie.timeline.util.duration.plus import java.io.StringReader import java.sql.Connection import java.time.Instant @@ -101,7 +102,7 @@ class AeriePostgresPlan( val index = result.binarySearch { a -> a.id.compareTo(it.start.parentId) } if (index >= 0) { val parent = result[index] - it.start.updateEstimate(parent.startTime + it.start.offset) + it.start.estimatedStart = parent.startTime + it.start.offset result.add(it) } else false } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulationResults.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulationResults.kt index 77bf472118..0dc05c2f08 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulationResults.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulationResults.kt @@ -9,6 +9,7 @@ import gov.nasa.jpl.aerie.timeline.collections.Instances /** An interface for querying plan information and simulation results. */ interface SimulationResults { + /** Whether these results are up-to-date with all changes. */ fun isStale(): Boolean /** Bounds on which the plan was most recently simulated. */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SqlUtils.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SqlUtils.kt index 94c220f0d9..896fea5ad8 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SqlUtils.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SqlUtils.kt @@ -3,7 +3,7 @@ package gov.nasa.jpl.aerie.timeline.plan import java.sql.Connection import java.sql.PreparedStatement -fun getSingleLongQueryResult(statement: PreparedStatement): Long { +internal fun getSingleLongQueryResult(statement: PreparedStatement): Long { val result = statement.executeQuery() if (!result.next()) throw AeriePostgresSimulationResults.DatabaseError("Expected exactly one result for query, found none: $statement") val int = result.getLong(1) @@ -11,4 +11,4 @@ fun getSingleLongQueryResult(statement: PreparedStatement): Long { return int } -fun intervalStyleStatement(c: Connection): PreparedStatement = c.prepareStatement("set intervalstyle = 'iso_8601';") +internal fun intervalStyleStatement(c: Connection): PreparedStatement = c.prepareStatement("set intervalstyle = 'iso_8601';") diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2Test.kt b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2Test.kt index 5810ebbcf8..93f337f89d 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2Test.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2Test.kt @@ -2,11 +2,12 @@ package gov.nasa.jpl.aerie.timeline.util import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.NullBinaryOperation -import gov.nasa.jpl.aerie.timeline.durationUtils.* import gov.nasa.jpl.aerie.timeline.Interval import gov.nasa.jpl.aerie.timeline.Interval.Companion.between import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.* import gov.nasa.jpl.aerie.timeline.payloads.Segment +import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.jpl.aerie.timeline.util.duration.rangeUntil import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* From 1099d97ea12feacfece707ddeb5fd1dd4cccd299 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Fri, 26 Apr 2024 12:33:25 -0700 Subject: [PATCH 007/108] Add create overload --- .../procedural/scheduling/plan/EditablePlan.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt index eb7531f67b..4b64c0ad08 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt @@ -1,6 +1,9 @@ package gov.nasa.jpl.aerie.procedural.scheduling.plan +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.procedural.scheduling.simulation.SimulateOptions +import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective +import gov.nasa.jpl.aerie.timeline.payloads.activities.DirectiveStart import gov.nasa.jpl.aerie.timeline.plan.Plan import gov.nasa.jpl.aerie.timeline.plan.SimulationResults @@ -17,6 +20,19 @@ interface EditablePlan: Plan { */ fun create(directive: NewDirective): Long + /** A simplified version of [create] with minimal arguments. */ + fun create( + type: String, + start: DirectiveStart, + arguments: Map + ) = create(NewDirective( + AnyDirective(arguments), + "Unnamed Activity", + type, + start + )) + + /** Commit plan edits, making them final. */ fun commit() From 19bceafc7b97ad2353ab4fc8f5167af2d6e38643 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 9 May 2024 15:50:53 -0700 Subject: [PATCH 008/108] Move plan and sim result implementations to remote package --- e2e-tests/build.gradle | 1 + .../jpl/aerie/e2e/TimelineRemoteTests.java | 4 +- procedural/remote/MODULE_DOCS.md | 8 +++ procedural/remote/build.gradle | 59 +++++++++++++++++++ .../procedural/remote}/AeriePostgresPlan.kt | 35 ++++++----- .../remote}/AeriePostgresSimulationResults.kt | 19 ++++-- .../jpl/aerie/procedural/remote}/SqlUtils.kt | 2 +- procedural/scheduling/build.gradle | 1 - procedural/timeline/build.gradle | 1 - .../payloads/activities/AnyInstance.kt | 7 ++- settings.gradle | 1 + 11 files changed, 110 insertions(+), 28 deletions(-) create mode 100644 procedural/remote/MODULE_DOCS.md create mode 100644 procedural/remote/build.gradle rename procedural/{timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan => remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote}/AeriePostgresPlan.kt (87%) rename procedural/{timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan => remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote}/AeriePostgresSimulationResults.kt (91%) rename procedural/{timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan => remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote}/SqlUtils.kt (93%) diff --git a/e2e-tests/build.gradle b/e2e-tests/build.gradle index 1ba70ac133..5c2ec884aa 100644 --- a/e2e-tests/build.gradle +++ b/e2e-tests/build.gradle @@ -55,6 +55,7 @@ task e2eTest(type: Test) { dependencies { testImplementation project(":procedural:timeline") + testImplementation project(":procedural:remote") testImplementation "com.zaxxer:HikariCP:5.1.0" testImplementation("org.postgresql:postgresql:42.6.0") testImplementation project(':merlin-driver') diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java index 37c7408fcb..5d1bed518b 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java @@ -10,8 +10,8 @@ import gov.nasa.jpl.aerie.timeline.collections.profiles.Real; import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation; import gov.nasa.jpl.aerie.timeline.payloads.Segment; -import gov.nasa.jpl.aerie.timeline.plan.AeriePostgresPlan; -import gov.nasa.jpl.aerie.timeline.plan.AeriePostgresSimulationResults; +import gov.nasa.jpl.aerie.procedural.remote.AeriePostgresPlan; +import gov.nasa.jpl.aerie.procedural.remote.AeriePostgresSimulationResults; import gov.nasa.jpl.aerie.timeline.plan.SimulatedPlan; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; diff --git a/procedural/remote/MODULE_DOCS.md b/procedural/remote/MODULE_DOCS.md new file mode 100644 index 0000000000..9eb37ad2bb --- /dev/null +++ b/procedural/remote/MODULE_DOCS.md @@ -0,0 +1,8 @@ +# Module Remote + +This library provides tools for accessing plans and simulation results from remote execution environments. +This is intended to allow goal and constraint authors to run their code outside Aerie, and these implementations +do not need to be packaged in the goal and constraint jars. + +# Package gov.nasa.jpl.aerie.procedural.remote +Remote utilities for running goals and constraints outside of Aerie. diff --git a/procedural/remote/build.gradle b/procedural/remote/build.gradle new file mode 100644 index 0000000000..73c1f742af --- /dev/null +++ b/procedural/remote/build.gradle @@ -0,0 +1,59 @@ +import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile + +plugins { + id 'com.github.johnrengelman.shadow' version '8.1.1' + id "org.jetbrains.kotlin.jvm" version "1.9.22" + id 'java-library' + id 'org.jetbrains.dokka' version '1.9.10' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation project(':procedural:scheduling') + implementation project(':procedural:timeline') + implementation project(':merlin-driver') + implementation project(':scheduler-driver') + implementation project(':parsing-utilities') + + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' + testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +tasks.withType(KotlinJvmCompile.class).configureEach { + jvmTargetValidationMode = JvmTargetValidationMode.WARNING +} + +tasks.named('test') { + useJUnitPlatform() +} + +kotlin { + jvmToolchain(21) +} + +var timelineSource = "${project(":procedural:timeline").projectDir}/src/main/kotlin" +var schedulingSource = "${project(":procedural:scheduling").projectDir}/src/main/kotlin" + +dokkaHtmlPartial.configure { + dokkaSourceSets { + configureEach { + // used as project name in the header + moduleName.set("Remote") + + reportUndocumented.set(true) + + // contains descriptions for the module and the packages + includes.from("MODULE_DOCS.md") + + sourceRoots.from(timelineSource) + suppressedFiles.from(timelineSource) + sourceRoots.from(schedulingSource) + suppressedFiles.from(schedulingSource) + } + } +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresPlan.kt b/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresPlan.kt similarity index 87% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresPlan.kt rename to procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresPlan.kt index d2258b6238..a052e2da36 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresPlan.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresPlan.kt @@ -1,38 +1,43 @@ -package gov.nasa.jpl.aerie.timeline.plan +package gov.nasa.jpl.aerie.procedural.remote -import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser +import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser.serializedValueP +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.timeline.BaseTimeline -import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.BoundsTransformer import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive import gov.nasa.jpl.aerie.timeline.collections.Directives +import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive import gov.nasa.jpl.aerie.timeline.payloads.activities.DirectiveStart -import gov.nasa.jpl.aerie.timeline.util.duration.minus +import gov.nasa.jpl.aerie.timeline.plan.Plan import gov.nasa.jpl.aerie.timeline.util.duration.plus +import gov.nasa.jpl.aerie.timeline.util.duration.minus import java.io.StringReader import java.sql.Connection import java.time.Instant import javax.json.Json -/** A connection to Aerie's database for a particular simulation result. */ -class AeriePostgresPlan( - /** A connection to Aerie's database. */ +/** + * A connection to Aerie's database for a particular simulation result. + * + * @param c A connection to Aerie's database + * @param planId The ID of the plan as specified by in the postgres database + */ +data class AeriePostgresPlan( private val c: Connection, - private val id: Int + private val planId: Int ): Plan { private val planInfo by lazy { val statement = c.prepareStatement("select start_time, duration from merlin.plan where id = ?;") - statement.setInt(1, id) + statement.setInt(1, planId) intervalStyleStatement(c).execute() val response = statement.executeQuery() if (!response.next()) throw DatabaseError("Expected exactly one result for query, found none: $statement") val result = object { val startTime = response.getTimestamp(1).toInstant() val duration = Duration.parseISO8601(response.getString(2)) - val id = this@AeriePostgresPlan.id + val id = this@AeriePostgresPlan.planId } if (response.next()) throw DatabaseError("Expected exactly one result for query, found more than one: $statement") result @@ -48,7 +53,7 @@ class AeriePostgresPlan( private fun parseJson(jsonStr: String): SerializedValue = Json.createReader(StringReader(jsonStr)).use { reader -> val requestJson = reader.readValue() - val result = SerializedValueJsonParser.serializedValueP.parse(requestJson) + val result = serializedValueP.parse(requestJson) return result.getSuccessOrThrow { DatabaseError(it.toString()) } } @@ -94,15 +99,15 @@ class AeriePostgresPlan( while (unresolved.size != 0) { val sizeAtStartOfStep = unresolved.size unresolved = unresolved.filterNot { - when (it.start) { + when (val s = it.start) { is DirectiveStart.Absolute -> { result.add(it) } is DirectiveStart.Anchor -> { - val index = result.binarySearch { a -> a.id.compareTo(it.start.parentId) } + val index = result.binarySearch { a -> a.id.compareTo(s.parentId) } if (index >= 0) { val parent = result[index] - it.start.estimatedStart = parent.startTime + it.start.offset + s.estimatedStart = parent.startTime + s.offset result.add(it) } else false } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresSimulationResults.kt b/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresSimulationResults.kt similarity index 91% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresSimulationResults.kt rename to procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresSimulationResults.kt index ea89145981..d0832e2726 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/AeriePostgresSimulationResults.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresSimulationResults.kt @@ -1,6 +1,6 @@ -package gov.nasa.jpl.aerie.timeline.plan +package gov.nasa.jpl.aerie.procedural.remote -import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser +import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser.serializedValueP import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.timeline.Interval.Companion.between @@ -10,14 +10,23 @@ import gov.nasa.jpl.aerie.timeline.collections.Instances import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegmentsOp import gov.nasa.jpl.aerie.timeline.payloads.Segment import gov.nasa.jpl.aerie.timeline.payloads.activities.Instance +import gov.nasa.jpl.aerie.timeline.plan.Plan +import gov.nasa.jpl.aerie.timeline.plan.SimulationResults import java.io.StringReader import java.sql.Connection import javax.json.Json import kotlin.jvm.optionals.getOrNull -/** A connection to Aerie's database for a particular simulation result. */ +/** + * A connection to Aerie's database for a particular simulation result. + * + * @param c A connection to Aerie's database + * @param simDatasetId The simulation dataset id to query for (this is different from the dataset id) + * @param plan a plan object representing the plan associated with this simulation dataset + * @param stale whether these results are not up-to-date + */ data class AeriePostgresSimulationResults( - /** A connection to Aerie's database. */ + /** */ private val c: Connection, /** The particular simulation dataset to query. */ private val simDatasetId: Int, @@ -126,7 +135,7 @@ data class AeriePostgresSimulationResults( private fun parseJson(jsonStr: String): SerializedValue = Json.createReader(StringReader(jsonStr)).use { reader -> val requestJson = reader.readValue() - val result = SerializedValueJsonParser.serializedValueP.parse(requestJson) + val result = serializedValueP.parse(requestJson) return result.getSuccessOrThrow { DatabaseError(it.toString()) } } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SqlUtils.kt b/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/SqlUtils.kt similarity index 93% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SqlUtils.kt rename to procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/SqlUtils.kt index 896fea5ad8..df5a052d4a 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SqlUtils.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/SqlUtils.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.timeline.plan +package gov.nasa.jpl.aerie.procedural.remote import java.sql.Connection import java.sql.PreparedStatement diff --git a/procedural/scheduling/build.gradle b/procedural/scheduling/build.gradle index 1a76137edc..08949fed2a 100644 --- a/procedural/scheduling/build.gradle +++ b/procedural/scheduling/build.gradle @@ -15,7 +15,6 @@ repositories { dependencies { implementation project(':procedural:timeline') implementation project(':merlin-driver') - implementation project(':scheduler-driver') testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' diff --git a/procedural/timeline/build.gradle b/procedural/timeline/build.gradle index 2c96fb3e42..63de0e53cb 100644 --- a/procedural/timeline/build.gradle +++ b/procedural/timeline/build.gradle @@ -14,7 +14,6 @@ repositories { dependencies { implementation project(':merlin-driver') - implementation project(':parsing-utilities') testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyInstance.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyInstance.kt index 1ec6569818..bf951053b8 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyInstance.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyInstance.kt @@ -1,7 +1,6 @@ package gov.nasa.jpl.aerie.timeline.payloads.activities import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.timeline.plan.AeriePostgresPlan import kotlin.jvm.optionals.getOrNull /** A general-purpose container for representing the arguments and computed attributes of any type of activity instance. */ @@ -14,10 +13,12 @@ data class AnyInstance( * Converts a [SerializedValue] object containing activity arguments and computed attributes to an [AnyInstance] object. */ fun deserialize(attributes: SerializedValue): AnyInstance { + /***/ class InstanceDeserializeError(message: String): Error(message) + val arguments = attributes.asMap().getOrNull()!!["arguments"]?.asMap()?.getOrNull() - ?: throw AeriePostgresPlan.DatabaseError("Could not get arguments from attributes: $attributes") + ?: throw InstanceDeserializeError("Could not get arguments from attributes: $attributes") val computedAttributes = attributes.asMap().getOrNull()!!["computedAttributes"] - ?: throw AeriePostgresPlan.DatabaseError("Could not get computed attributes from attributes: $attributes") + ?: throw InstanceDeserializeError("Could not get computed attributes from attributes: $attributes") return AnyInstance(arguments, computedAttributes) } } diff --git a/settings.gradle b/settings.gradle index 2b487ff64c..9853b119d3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,6 +16,7 @@ include 'permissions' include 'procedural:timeline' include 'procedural:constraints' include 'procedural:scheduling' +include 'procedural:remote' // Services for deployment within the Aerie infrastructure include 'merlin-server' From d5a0d7ea281f26f63c2c25d9b73605830c06260b Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Tue, 21 May 2024 15:12:35 -0700 Subject: [PATCH 009/108] Rename Procedure to Rule --- .../aerie/procedural/scheduling/{Procedure.kt => Rule.kt} | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/{Procedure.kt => Rule.kt} (59%) diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Procedure.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Rule.kt similarity index 59% rename from procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Procedure.kt rename to procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Rule.kt index ae49516ea8..3f665fb2cf 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Procedure.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Rule.kt @@ -1,12 +1,11 @@ package gov.nasa.jpl.aerie.procedural.scheduling import gov.nasa.jpl.aerie.procedural.scheduling.plan.EditablePlan -import gov.nasa.jpl.aerie.timeline.CollectOptions -/** The interface that all scheduling procedures must satisfy. */ -interface Procedure { +/** The interface that all scheduling rules must satisfy. */ +interface Rule { /** - * Run the procedure. + * Run the rule. * * @param plan A plan representation that can be edited and simulated. */ From f8aa585194b710d12fc5787d197e26125b38098c Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Wed, 7 Aug 2024 09:50:19 -0700 Subject: [PATCH 010/108] Fix simulation results identity comparison bug --- .../scheduler/worker/services/SynchronousSchedulerAgent.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java index 56a1af35e2..e9a68bf28c 100644 --- a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java +++ b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java @@ -259,7 +259,9 @@ public void schedule( planMetadataAfterChanges, uploadIdMap ); - } else if (simulationFacade.getLatestSimulationData().isPresent() && simulationFacade.getLatestSimulationData() != problem.getInitialSimulationResults()) { + } else if (simulationFacade.getLatestSimulationData().isPresent() + && problem.getInitialSimulationResults().isPresent() + && simulationFacade.getLatestSimulationData().get() != problem.getInitialSimulationResults().get()) { final var latest = simulationFacade.getLatestSimulationData().get(); datasetId = storeSimulationResults( latest, From b2bd1e6a7ba642d54011bd34f53a3b24d0e7919b Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Tue, 6 Aug 2024 15:28:49 -0700 Subject: [PATCH 011/108] Move timeline library to ammos.aerie.procedural --- .../jpl/aerie/e2e/TimelineRemoteTests.java | 10 +++---- .../procedural/constraints/Constraint.kt | 4 +-- .../aerie/procedural/constraints/Violation.kt | 4 +-- .../procedural/constraints/Violations.kt | 24 ++++++++-------- .../procedural/remote/AeriePostgresPlan.kt | 18 ++++++------ .../remote/AeriePostgresSimulationResults.kt | 18 ++++++------ .../aerie/procedural/scheduling/plan/Edit.kt | 4 +-- .../scheduling/plan/EditablePlan.kt | 8 +++--- .../scheduling/plan/NewDirective.kt | 6 ++-- procedural/timeline/MODULE_DOCS.md | 18 ++++++------ .../procedural}/timeline/BaseTimeline.kt | 4 +-- .../procedural}/timeline/BoundsTransformer.kt | 2 +- .../procedural}/timeline/CollectOptions.kt | 2 +- .../aerie/procedural}/timeline/Interval.kt | 6 ++-- .../timeline/NullBinaryOperation.kt | 4 +-- .../aerie/procedural}/timeline/Timeline.kt | 10 +++---- .../timeline/collections/Directives.kt | 12 ++++---- .../timeline/collections/Instances.kt | 14 +++++----- .../timeline/collections/Intervals.kt | 19 +++++++++++++ .../timeline/collections/Windows.kt | 18 ++++++------ .../timeline/collections/profiles/Booleans.kt | 20 ++++++------- .../collections/profiles/Constants.kt | 18 ++++++------ .../timeline/collections/profiles/Numbers.kt | 20 ++++++------- .../timeline/collections/profiles/Real.kt | 22 +++++++-------- .../procedural}/timeline/ops/ActivityOps.kt | 4 +-- .../procedural}/timeline/ops/BooleanOps.kt | 10 +++---- .../procedural}/timeline/ops/ConstantOps.kt | 2 +- .../procedural}/timeline/ops/GeneralOps.kt | 24 ++++++++-------- .../timeline/ops/NonZeroDurationOps.kt | 14 +++++----- .../procedural}/timeline/ops/ParallelOps.kt | 26 ++++++++--------- .../procedural}/timeline/ops/SegmentOps.kt | 12 ++++---- .../timeline/ops/SerialConstantOps.kt | 8 +++--- .../procedural}/timeline/ops/SerialOps.kt | 4 +-- .../timeline/ops/SerialSegmentOps.kt | 24 ++++++++-------- .../ops/coalesce/CoalesceIntervalsOp.kt | 10 +++++++ .../timeline/ops/coalesce/CoalesceNoOp.kt | 9 ++++++ .../ops/coalesce/CoalesceSegmentsOp.kt | 6 ++-- .../timeline/ops/numeric/LinearOps.kt | 10 +++---- .../timeline/ops/numeric/NumericOps.kt | 6 ++-- .../ops/numeric/PrimitiveNumberOps.kt | 8 +++--- .../timeline/ops/numeric/SerialNumericOps.kt | 12 ++++---- .../timeline/payloads/Connection.kt | 4 +-- .../timeline/payloads/IntervalLike.kt | 4 +-- .../timeline/payloads/LinearEquation.kt | 14 +++++----- .../procedural}/timeline/payloads/Segment.kt | 6 ++-- .../timeline/payloads/activities/Activity.kt | 4 +-- .../payloads/activities/AnyDirective.kt | 2 +- .../payloads/activities/AnyInstance.kt | 2 +- .../timeline/payloads/activities/Directive.kt | 4 +-- .../payloads/activities/DirectiveStart.kt | 2 +- .../timeline/payloads/activities/Instance.kt | 4 +-- .../aerie/procedural}/timeline/plan/Plan.kt | 8 +++--- .../timeline/plan/SimulatedPlan.kt | 2 +- .../timeline/plan/SimulationResults.kt | 12 ++++---- .../procedural}/timeline/util/Coalesce.kt | 6 ++-- .../procedural}/timeline/util/ListUtils.kt | 8 +++--- .../aerie/procedural}/timeline/util/Map2.kt | 12 ++++---- .../timeline/util/duration/Duration.kt | 4 +-- .../aerie/timeline/collections/Intervals.kt | 19 ------------- .../ops/coalesce/CoalesceIntervalsOp.kt | 10 ------- .../timeline/ops/coalesce/CoalesceNoOp.kt | 9 ------ .../timeline/BoundsTransformerTest.kt | 4 +-- .../procedural}/timeline/IntervalTest.kt | 16 +++++------ .../aerie/procedural}/timeline/SegmentTest.kt | 8 +++--- .../timeline/collections/WindowsTest.kt | 14 +++++----- .../collections/profiles/BooleansTest.kt | 12 ++++---- .../collections/profiles/NumbersTest.kt | 8 +++--- .../timeline/collections/profiles/RealTest.kt | 8 +++--- .../timeline/ops/GeneralOpsTest.kt | 28 +++++++++---------- .../timeline/ops/NonZeroDurationOpsTest.kt | 16 +++++------ .../timeline/ops/ParallelOpsTest.kt | 22 +++++++-------- .../timeline/ops/SerialConstantTest.kt | 16 +++++------ .../timeline/ops/SerialSegmentOpsTest.kt | 20 ++++++------- .../timeline/ops/numeric/LinearOpsTest.kt | 10 +++---- .../ops/numeric/PrimitiveNumberOpsTest.kt | 8 +++--- .../ops/numeric/SerialNumericOpsTest.kt | 18 ++++++------ .../procedural}/timeline/util/CoalesceTest.kt | 12 ++++---- .../timeline/util/LinearEquationTest.kt | 14 +++++----- .../timeline/util/ListCollectorTest.kt | 10 +++---- .../procedural}/timeline/util/Map2Test.kt | 16 +++++------ 80 files changed, 435 insertions(+), 435 deletions(-) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/BaseTimeline.kt (83%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/BoundsTransformer.kt (95%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/CollectOptions.kt (92%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/Interval.kt (98%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/NullBinaryOperation.kt (96%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/Timeline.kt (76%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/collections/Directives.kt (55%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/collections/Instances.kt (53%) create mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Intervals.kt rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/collections/Windows.kt (66%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/collections/profiles/Booleans.kt (86%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/collections/profiles/Constants.kt (57%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/collections/profiles/Numbers.kt (93%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/collections/profiles/Real.kt (93%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/ActivityOps.kt (77%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/BooleanOps.kt (79%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/ConstantOps.kt (94%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/GeneralOps.kt (94%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/NonZeroDurationOps.kt (84%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/ParallelOps.kt (91%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/SegmentOps.kt (92%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/SerialConstantOps.kt (84%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/SerialOps.kt (64%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/SerialSegmentOps.kt (90%) create mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/coalesce/CoalesceIntervalsOp.kt create mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/coalesce/CoalesceNoOp.kt rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/coalesce/CoalesceSegmentsOp.kt (52%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/numeric/LinearOps.kt (81%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/numeric/NumericOps.kt (72%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/numeric/PrimitiveNumberOps.kt (88%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/numeric/SerialNumericOps.kt (84%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/payloads/Connection.kt (79%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/payloads/IntervalLike.kt (79%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/payloads/LinearEquation.kt (92%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/payloads/Segment.kt (91%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/payloads/activities/Activity.kt (69%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/payloads/activities/AnyDirective.kt (90%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/payloads/activities/AnyInstance.kt (94%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/payloads/activities/Directive.kt (90%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/payloads/activities/DirectiveStart.kt (95%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/payloads/activities/Instance.kt (86%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/plan/Plan.kt (82%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/plan/SimulatedPlan.kt (80%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/plan/SimulationResults.kt (76%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/util/Coalesce.kt (93%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/util/ListUtils.kt (89%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/util/Map2.kt (95%) rename procedural/timeline/src/main/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/util/duration/Duration.kt (93%) delete mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Intervals.kt delete mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/coalesce/CoalesceIntervalsOp.kt delete mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/coalesce/CoalesceNoOp.kt rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/BoundsTransformerTest.kt (75%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/IntervalTest.kt (91%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/SegmentTest.kt (59%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/collections/WindowsTest.kt (85%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/collections/profiles/BooleansTest.kt (82%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/collections/profiles/NumbersTest.kt (70%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/collections/profiles/RealTest.kt (67%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/GeneralOpsTest.kt (84%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/NonZeroDurationOpsTest.kt (76%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/ParallelOpsTest.kt (67%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/SerialConstantTest.kt (65%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/SerialSegmentOpsTest.kt (68%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/numeric/LinearOpsTest.kt (58%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/numeric/PrimitiveNumberOpsTest.kt (81%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/ops/numeric/SerialNumericOpsTest.kt (79%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/util/CoalesceTest.kt (70%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/util/LinearEquationTest.kt (81%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/util/ListCollectorTest.kt (83%) rename procedural/timeline/src/test/kotlin/gov/nasa/{jpl/aerie => ammos/aerie/procedural}/timeline/util/Map2Test.kt (92%) diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java index 5d1bed518b..99222c7a37 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java @@ -6,13 +6,13 @@ import gov.nasa.jpl.aerie.e2e.utils.GatewayRequests; import gov.nasa.jpl.aerie.e2e.utils.HasuraRequests; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; -import gov.nasa.jpl.aerie.timeline.Interval; -import gov.nasa.jpl.aerie.timeline.collections.profiles.Real; -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation; -import gov.nasa.jpl.aerie.timeline.payloads.Segment; +import gov.nasa.ammos.aerie.procedural.timeline.Interval; +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment; import gov.nasa.jpl.aerie.procedural.remote.AeriePostgresPlan; import gov.nasa.jpl.aerie.procedural.remote.AeriePostgresSimulationResults; -import gov.nasa.jpl.aerie.timeline.plan.SimulatedPlan; +import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulatedPlan; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt b/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt index 01e992f7c7..c6dd41e8c1 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.procedural.constraints -import gov.nasa.jpl.aerie.timeline.CollectOptions -import gov.nasa.jpl.aerie.timeline.plan.SimulatedPlan +import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions +import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulatedPlan /** The interface that all constraints must satisfy. */ interface Constraint { diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violation.kt b/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violation.kt index 9e51a81e08..7dc7b81fc6 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violation.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violation.kt @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.procedural.constraints -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike /** A single violation of a constraint. */ data class Violation( diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violations.kt b/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violations.kt index 43af1ee4ac..1f9378e926 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violations.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violations.kt @@ -2,18 +2,18 @@ package gov.nasa.jpl.aerie.procedural.constraints import gov.nasa.jpl.aerie.procedural.constraints.ActivityId.DirectiveId import gov.nasa.jpl.aerie.procedural.constraints.ActivityId.InstanceId -import gov.nasa.jpl.aerie.timeline.BaseTimeline -import gov.nasa.jpl.aerie.timeline.BoundsTransformer -import gov.nasa.jpl.aerie.timeline.Timeline -import gov.nasa.jpl.aerie.timeline.collections.Intervals -import gov.nasa.jpl.aerie.timeline.collections.Windows -import gov.nasa.jpl.aerie.timeline.collections.profiles.Real -import gov.nasa.jpl.aerie.timeline.ops.* -import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceNoOp -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike -import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive -import gov.nasa.jpl.aerie.timeline.payloads.activities.Instance -import gov.nasa.jpl.aerie.timeline.util.preprocessList +import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline +import gov.nasa.ammos.aerie.procedural.timeline.BoundsTransformer +import gov.nasa.ammos.aerie.procedural.timeline.Timeline +import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.collections.Windows +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real +import gov.nasa.ammos.aerie.procedural.timeline.ops.* +import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceNoOp +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Instance +import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList /** A timeline of [Violations][Violation]. */ data class Violations(private val timeline: Timeline): diff --git a/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresPlan.kt b/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresPlan.kt index a052e2da36..2f02617ffc 100644 --- a/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresPlan.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresPlan.kt @@ -3,15 +3,15 @@ package gov.nasa.jpl.aerie.procedural.remote import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser.serializedValueP import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.timeline.BaseTimeline -import gov.nasa.jpl.aerie.timeline.BoundsTransformer -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.collections.Directives -import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive -import gov.nasa.jpl.aerie.timeline.payloads.activities.DirectiveStart -import gov.nasa.jpl.aerie.timeline.plan.Plan -import gov.nasa.jpl.aerie.timeline.util.duration.plus -import gov.nasa.jpl.aerie.timeline.util.duration.minus +import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline +import gov.nasa.ammos.aerie.procedural.timeline.BoundsTransformer +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.collections.Directives +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart +import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.plus +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.minus import java.io.StringReader import java.sql.Connection import java.time.Instant diff --git a/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresSimulationResults.kt b/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresSimulationResults.kt index d0832e2726..717266035e 100644 --- a/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresSimulationResults.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresSimulationResults.kt @@ -3,15 +3,15 @@ package gov.nasa.jpl.aerie.procedural.remote import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser.serializedValueP import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive -import gov.nasa.jpl.aerie.timeline.collections.Instances -import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegmentsOp -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.payloads.activities.Instance -import gov.nasa.jpl.aerie.timeline.plan.Plan -import gov.nasa.jpl.aerie.timeline.plan.SimulationResults +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Exclusive +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Inclusive +import gov.nasa.ammos.aerie.procedural.timeline.collections.Instances +import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceSegmentsOp +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Instance +import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan +import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults import java.io.StringReader import java.sql.Connection import javax.json.Json diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt index a9c93a65ea..b33081ac04 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.procedural.scheduling.plan -import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective -import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive /** * Edits that can be made to the plan. diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt index 4b64c0ad08..1c35ef1d51 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt @@ -2,10 +2,10 @@ package gov.nasa.jpl.aerie.procedural.scheduling.plan import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.procedural.scheduling.simulation.SimulateOptions -import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective -import gov.nasa.jpl.aerie.timeline.payloads.activities.DirectiveStart -import gov.nasa.jpl.aerie.timeline.plan.Plan -import gov.nasa.jpl.aerie.timeline.plan.SimulationResults +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart +import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan +import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults /** A plan representation that can be edited and simulated. */ interface EditablePlan: Plan { diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt index 09f0df7b22..ffbd29d7e8 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt @@ -1,8 +1,8 @@ package gov.nasa.jpl.aerie.procedural.scheduling.plan -import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective -import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive -import gov.nasa.jpl.aerie.timeline.payloads.activities.DirectiveStart +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart /** A new directive to be created, which doesn't have an id yet. */ data class NewDirective( diff --git a/procedural/timeline/MODULE_DOCS.md b/procedural/timeline/MODULE_DOCS.md index a35bfc3aef..4d77768689 100644 --- a/procedural/timeline/MODULE_DOCS.md +++ b/procedural/timeline/MODULE_DOCS.md @@ -75,34 +75,34 @@ call `myProfile.convert(::Intervals)`. The `Intervals` timeline type is the most There are two options for representing numeric profiles: `Real` and `Numbers`. `Numbers` is piece-wise constant and can contain any primitive numeric type, while `Real` is piece-wise linear and can only contain doubles. `Real` is unique because it is the only profile type so far that represents values that vary within the segment, not just between segments. -# Package gov.nasa.jpl.aerie.timeline.collections +# Package gov.nasa.ammos.aerie.procedural.timeline.collections The officially supported timeline types. -# Package gov.nasa.jpl.aerie.timeline.collections.profiles +# Package gov.nasa.ammos.aerie.procedural.timeline.collections.profiles Timeline types for resource profiles. -# Package gov.nasa.jpl.aerie.timeline.ops +# Package gov.nasa.ammos.aerie.procedural.timeline.ops Operations mixins to be applied to timeline types. -# Package gov.nasa.jpl.aerie.timeline.ops.coalesce +# Package gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce Operations mixins for specifying whether a timeline should be coalesced. -# Package gov.nasa.jpl.aerie.timeline.ops.numeric +# Package gov.nasa.ammos.aerie.procedural.timeline.ops.numeric Operations mixins just for numeric types (`Real` and `Numbers`). -# Package gov.nasa.jpl.aerie.timeline.payloads +# Package gov.nasa.ammos.aerie.procedural.timeline.payloads Payload types (`IntervalLike` implementors) that can be contained in timelines. -# Package gov.nasa.jpl.aerie.timeline.payloads.activities +# Package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities Containers for representing activity directives and instants. Currently, there is no specialization for activity types, and all directives and instants use `AnyDirective` and `AnyInstance`, respectively. These classes represent arguments and computed attributes using `SerializedValue`. -# Package gov.nasa.jpl.aerie.timeline.plan +# Package gov.nasa.ammos.aerie.procedural.timeline.plan Tools for querying simulation results, activity directives, and general information from a plan. -# Package gov.nasa.jpl.aerie.timeline.util +# Package gov.nasa.ammos.aerie.procedural.timeline.util Common tools used by operations and timeline constructors to sanitize and process lists. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/BaseTimeline.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt similarity index 83% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/BaseTimeline.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt index 652ee64f18..3bfa0f181d 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/BaseTimeline.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt @@ -1,6 +1,6 @@ -package gov.nasa.jpl.aerie.timeline +package gov.nasa.ammos.aerie.procedural.timeline -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike /** * The basic timeline container that all higher-level timeline collections ultimately delegate to. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformer.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BoundsTransformer.kt similarity index 95% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformer.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BoundsTransformer.kt index c5debc7494..33fb663ddd 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformer.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BoundsTransformer.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.timeline +package gov.nasa.ammos.aerie.procedural.timeline import gov.nasa.jpl.aerie.merlin.protocol.types.Duration diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/CollectOptions.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/CollectOptions.kt similarity index 92% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/CollectOptions.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/CollectOptions.kt index 8fdf667d0b..16e62f6f34 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/CollectOptions.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/CollectOptions.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.timeline +package gov.nasa.ammos.aerie.procedural.timeline /** * Options for collecting a timeline. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Interval.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Interval.kt similarity index 98% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Interval.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Interval.kt index c8c8c10e0e..b286c92923 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Interval.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Interval.kt @@ -1,8 +1,8 @@ -package gov.nasa.jpl.aerie.timeline +package gov.nasa.ammos.aerie.procedural.timeline import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike /** * An Interval on the timeline, represented by start and end points diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/NullBinaryOperation.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/NullBinaryOperation.kt similarity index 96% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/NullBinaryOperation.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/NullBinaryOperation.kt index 11fe177975..0e78b40db0 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/NullBinaryOperation.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/NullBinaryOperation.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.timeline +package gov.nasa.ammos.aerie.procedural.timeline /** * A generalized binary operation interface for maybe-null operands. @@ -80,7 +80,7 @@ fun interface NullBinaryOperation { * @param convert Converts an input into the accumulator. * @param combine Combines the accumulator with a new input. * - * See [gov.nasa.jpl.aerie.timeline.ops.ParallelOps.reduceIntoProfile] for an example of where it should be used. + * See [gov.nasa.ammos.aerie.procedural.timeline.ops.ParallelOps.reduceIntoProfile] for an example of where it should be used. */ @JvmStatic fun reduce( convert: (new: In & Any, Interval) -> Out, diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Timeline.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt similarity index 76% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Timeline.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt index ca77ab8f24..8a8f666c90 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Timeline.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt @@ -1,6 +1,6 @@ -package gov.nasa.jpl.aerie.timeline +package gov.nasa.ammos.aerie.procedural.timeline -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike /** * Interface of the raw timeline object. @@ -37,9 +37,9 @@ interface Timeline, THIS: Timeline> { * The payload type [V] must be equal between the two types. * * This operation allows you to break the invariants of more specialized timeline types. For example, casting - * [`Intervals>`][gov.nasa.jpl.aerie.timeline.collections.Intervals] to [Booleans][gov.nasa.jpl.aerie.timeline.collections.profiles.Booleans] + * [`Intervals>`][gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals] to [Booleans][gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans] * without sorting and coalescing the segments could result in an invalid profile. In cases like that, it is better to - * use [flattenIntoProfile][gov.nasa.jpl.aerie.timeline.ops.ParallelOps.flattenIntoProfile] or [reduceIntoProfile][gov.nasa.jpl.aerie.timeline.ops.ParallelOps.reduceIntoProfile]. + * use [flattenIntoProfile][gov.nasa.ammos.aerie.procedural.timeline.ops.ParallelOps.flattenIntoProfile] or [reduceIntoProfile][gov.nasa.ammos.aerie.procedural.timeline.ops.ParallelOps.reduceIntoProfile]. */ fun > unsafeCast(ctor: (Timeline) -> RESULT): RESULT @@ -50,7 +50,7 @@ interface Timeline, THIS: Timeline> { * and therefore don't need the user to provide a constructor manually. * * It is highly unlikely that users will ever need to access this property, - * because the [gov.nasa.jpl.aerie.timeline.ops.GeneralOps.unsafeOperate] function accesses this for them. + * because the [gov.nasa.ammos.aerie.procedural.timeline.ops.GeneralOps.unsafeOperate] function accesses this for them. * * @suppress */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Directives.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Directives.kt similarity index 55% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Directives.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Directives.kt index 05ae361d5f..ea10090036 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Directives.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Directives.kt @@ -1,10 +1,10 @@ -package gov.nasa.jpl.aerie.timeline.collections +package gov.nasa.ammos.aerie.procedural.timeline.collections -import gov.nasa.jpl.aerie.timeline.BaseTimeline -import gov.nasa.jpl.aerie.timeline.payloads.activities.Directive -import gov.nasa.jpl.aerie.timeline.Timeline -import gov.nasa.jpl.aerie.timeline.ops.ActivityOps -import gov.nasa.jpl.aerie.timeline.util.preprocessList +import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive +import gov.nasa.ammos.aerie.procedural.timeline.Timeline +import gov.nasa.ammos.aerie.procedural.timeline.ops.ActivityOps +import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList /** * A timeline of activity directives. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Instances.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Instances.kt similarity index 53% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Instances.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Instances.kt index 4779f413be..2ff7b8857e 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Instances.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Instances.kt @@ -1,11 +1,11 @@ -package gov.nasa.jpl.aerie.timeline.collections +package gov.nasa.ammos.aerie.procedural.timeline.collections -import gov.nasa.jpl.aerie.timeline.Timeline -import gov.nasa.jpl.aerie.timeline.BaseTimeline -import gov.nasa.jpl.aerie.timeline.payloads.activities.Instance -import gov.nasa.jpl.aerie.timeline.ops.* -import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceNoOp -import gov.nasa.jpl.aerie.timeline.util.preprocessList +import gov.nasa.ammos.aerie.procedural.timeline.Timeline +import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Instance +import gov.nasa.ammos.aerie.procedural.timeline.ops.* +import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceNoOp +import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList /** * A timeline of activity instances. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Intervals.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Intervals.kt new file mode 100644 index 0000000000..40fcc68184 --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Intervals.kt @@ -0,0 +1,19 @@ +package gov.nasa.ammos.aerie.procedural.timeline.collections + +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline +import gov.nasa.ammos.aerie.procedural.timeline.ops.ParallelOps +import gov.nasa.ammos.aerie.procedural.timeline.Timeline +import gov.nasa.ammos.aerie.procedural.timeline.ops.NonZeroDurationOps +import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceNoOp +import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList + +/** A timeline of any [IntervalLike] object with no special operations. */ +data class Intervals>(private val timeline: Timeline>): + Timeline> by timeline, + ParallelOps>, + NonZeroDurationOps> +{ + constructor(vararg intervals: T): this(intervals.asList()) + constructor(intervals: List): this(BaseTimeline(::Intervals, preprocessList(intervals, null))) +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Windows.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Windows.kt similarity index 66% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Windows.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Windows.kt index 4b03e84b52..b54f30ef2d 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Windows.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Windows.kt @@ -1,13 +1,13 @@ -package gov.nasa.jpl.aerie.timeline.collections +package gov.nasa.ammos.aerie.procedural.timeline.collections -import gov.nasa.jpl.aerie.timeline.BaseTimeline -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.Timeline -import gov.nasa.jpl.aerie.timeline.ops.NonZeroDurationOps -import gov.nasa.jpl.aerie.timeline.ops.SerialOps -import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceIntervalsOp -import gov.nasa.jpl.aerie.timeline.util.preprocessList -import gov.nasa.jpl.aerie.timeline.util.sorted +import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.Timeline +import gov.nasa.ammos.aerie.procedural.timeline.ops.NonZeroDurationOps +import gov.nasa.ammos.aerie.procedural.timeline.ops.SerialOps +import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceIntervalsOp +import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList +import gov.nasa.ammos.aerie.procedural.timeline.util.sorted /** A coalescing timeline of [Intervals][Interval] with no extra data. */ data class Windows(private val timeline: Timeline): diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Booleans.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Booleans.kt similarity index 86% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Booleans.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Booleans.kt index fbf569536c..709549bddf 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Booleans.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Booleans.kt @@ -1,14 +1,14 @@ -package gov.nasa.jpl.aerie.timeline.collections.profiles +package gov.nasa.ammos.aerie.procedural.timeline.collections.profiles import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.timeline.* -import gov.nasa.jpl.aerie.timeline.util.duration.unaryMinus -import gov.nasa.jpl.aerie.timeline.ops.BooleanOps -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.ops.SerialConstantOps -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation -import gov.nasa.jpl.aerie.timeline.util.preprocessList +import gov.nasa.ammos.aerie.procedural.timeline.* +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.unaryMinus +import gov.nasa.ammos.aerie.procedural.timeline.ops.BooleanOps +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.ops.SerialConstantOps +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation +import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList /** A profile of booleans. */ data class Booleans(private val timeline: Timeline, Booleans>): @@ -86,7 +86,7 @@ data class Booleans(private val timeline: Timeline, Booleans>): * @param unit base unit of time to count. As in, the resulting real profile will increase by * `1` for each `unit` duration spent in the `true` state. * - * @see gov.nasa.jpl.aerie.timeline.ops.numeric.SerialNumericOps.integrate for further explanation of [unit]. + * @see gov.nasa.ammos.aerie.procedural.timeline.ops.numeric.SerialNumericOps.integrate for further explanation of [unit]. */ fun accumulatedTrueDuration(unit: Duration) = mapValues(::Real) { LinearEquation(if (it.value) 1.0 else 0.0) }.integrate(unit) @@ -117,7 +117,7 @@ data class Booleans(private val timeline: Timeline, Booleans>): /***/ companion object { /** * Converts a list of serialized value segments into a [Booleans] profile; - * for use with [gov.nasa.jpl.aerie.timeline.plan.Plan.resource]. + * for use with [gov.nasa.ammos.aerie.procedural.timeline.plan.Plan.resource]. */ @JvmStatic fun deserialize(list: List>) = Booleans(list.map { it.withNewValue(it.value.asBoolean().get()) }) } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Constants.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Constants.kt similarity index 57% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Constants.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Constants.kt index 4a56661570..827f18887f 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Constants.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Constants.kt @@ -1,13 +1,13 @@ -package gov.nasa.jpl.aerie.timeline.collections.profiles +package gov.nasa.ammos.aerie.procedural.timeline.collections.profiles import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.BaseTimeline -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.Timeline -import gov.nasa.jpl.aerie.timeline.ops.SerialConstantOps -import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegmentsOp -import gov.nasa.jpl.aerie.timeline.util.preprocessList +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.Timeline +import gov.nasa.ammos.aerie.procedural.timeline.ops.SerialConstantOps +import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceSegmentsOp +import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList /** A profile of piece-wise constant values. */ data class Constants(private val timeline: Timeline, Constants>): @@ -20,7 +20,7 @@ data class Constants(private val timeline: Timeline, Constant /***/ companion object { /** - * Delegates to the constructor, for use with [gov.nasa.jpl.aerie.timeline.plan.Plan.resource]. + * Delegates to the constructor, for use with [gov.nasa.ammos.aerie.procedural.timeline.plan.Plan.resource]. * * Does not convert the serialized values because it does not know what it should be converted into. * You will need to do that yourself using [mapValues]. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Numbers.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Numbers.kt similarity index 93% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Numbers.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Numbers.kt index c8b2a2e225..8342b2353c 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Numbers.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Numbers.kt @@ -1,15 +1,15 @@ -package gov.nasa.jpl.aerie.timeline.collections.profiles +package gov.nasa.ammos.aerie.procedural.timeline.collections.profiles import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.timeline.* -import gov.nasa.jpl.aerie.timeline.util.duration.unaryMinus -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.ops.SerialConstantOps -import gov.nasa.jpl.aerie.timeline.ops.numeric.PrimitiveNumberOps -import gov.nasa.jpl.aerie.timeline.ops.numeric.SerialNumericOps -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation -import gov.nasa.jpl.aerie.timeline.util.preprocessList +import gov.nasa.ammos.aerie.procedural.timeline.* +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.unaryMinus +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.ops.SerialConstantOps +import gov.nasa.ammos.aerie.procedural.timeline.ops.numeric.PrimitiveNumberOps +import gov.nasa.ammos.aerie.procedural.timeline.ops.numeric.SerialNumericOps +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation +import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList import java.lang.ArithmeticException import kotlin.math.pow @@ -205,7 +205,7 @@ data class Numbers(private val timeline: Timeline, Numbers /***/ companion object { /** * Converts a list of serialized value segments into a [Numbers] profile; - * for use with [gov.nasa.jpl.aerie.timeline.plan.Plan.resource]. + * for use with [gov.nasa.ammos.aerie.procedural.timeline.plan.Plan.resource]. * * Prefers converting to longs if possible, and falls back to doubles if not. */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Real.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt similarity index 93% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Real.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt index 630c9b506f..be61bade60 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/Real.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt @@ -1,16 +1,16 @@ -package gov.nasa.jpl.aerie.timeline.collections.profiles +package gov.nasa.ammos.aerie.procedural.timeline.collections.profiles import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.timeline.* -import gov.nasa.jpl.aerie.timeline.ops.numeric.LinearOps -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.ops.numeric.SerialNumericOps -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation -import gov.nasa.jpl.aerie.timeline.payloads.transpose -import gov.nasa.jpl.aerie.timeline.util.preprocessList -import gov.nasa.jpl.aerie.timeline.util.truncateList -import gov.nasa.jpl.aerie.timeline.util.duration.unaryMinus +import gov.nasa.ammos.aerie.procedural.timeline.* +import gov.nasa.ammos.aerie.procedural.timeline.ops.numeric.LinearOps +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.ops.numeric.SerialNumericOps +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation +import gov.nasa.ammos.aerie.procedural.timeline.payloads.transpose +import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList +import gov.nasa.ammos.aerie.procedural.timeline.util.truncateList +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.unaryMinus import kotlin.jvm.optionals.getOrNull import kotlin.math.pow @@ -185,7 +185,7 @@ data class Real(private val timeline: Timeline, Real>): /***/ companion object { /** - * Converts a list of serialized value segments into a real profile; for use with [gov.nasa.jpl.aerie.timeline.plan.Plan.resource]. + * Converts a list of serialized value segments into a real profile; for use with [gov.nasa.ammos.aerie.procedural.timeline.plan.Plan.resource]. * * Accepts either a map with the form `{initial: number, rate: number}`, or just a plain number for piecewise constant profiles. * While plain numbers are acceptable and will be converted to a [LinearEquation] without warning, consider using [Numbers] diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/ActivityOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ActivityOps.kt similarity index 77% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/ActivityOps.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ActivityOps.kt index fb5314106e..c97dde36d8 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/ActivityOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ActivityOps.kt @@ -1,6 +1,6 @@ -package gov.nasa.jpl.aerie.timeline.ops +package gov.nasa.ammos.aerie.procedural.timeline.ops -import gov.nasa.jpl.aerie.timeline.payloads.activities.Activity +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Activity /** * Operations mixin for timelines of activities. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/BooleanOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/BooleanOps.kt similarity index 79% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/BooleanOps.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/BooleanOps.kt index a7575dc92d..d137341bf2 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/BooleanOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/BooleanOps.kt @@ -1,14 +1,14 @@ -package gov.nasa.jpl.aerie.timeline.ops +package gov.nasa.ammos.aerie.procedural.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.collections.Intervals -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo /** * Operations mixin for timelines of booleans. * - * Currently only used by [gov.nasa.jpl.aerie.timeline.collections.profiles.Booleans], but could be used for + * Currently only used by [gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans], but could be used for * parallel boolean profiles in the future. */ interface BooleanOps>: ConstantOps { diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/ConstantOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ConstantOps.kt similarity index 94% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/ConstantOps.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ConstantOps.kt index 64103ae35d..b10129615e 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/ConstantOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ConstantOps.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.timeline.ops +package gov.nasa.ammos.aerie.procedural.timeline.ops /** * Operations mixin for segment-valued timelines whose payloads diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt similarity index 94% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt index 3f4ff9eee5..9295b7675e 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt @@ -1,15 +1,15 @@ -package gov.nasa.jpl.aerie.timeline.ops +package gov.nasa.ammos.aerie.procedural.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.timeline.* -import gov.nasa.jpl.aerie.timeline.collections.Intervals -import gov.nasa.jpl.aerie.timeline.collections.Windows -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.util.coalesceList -import gov.nasa.jpl.aerie.timeline.util.map2ParallelLists -import gov.nasa.jpl.aerie.timeline.util.sorted -import gov.nasa.jpl.aerie.timeline.util.truncateList +import gov.nasa.ammos.aerie.procedural.timeline.* +import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.collections.Windows +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.util.coalesceList +import gov.nasa.ammos.aerie.procedural.timeline.util.map2ParallelLists +import gov.nasa.ammos.aerie.procedural.timeline.util.sorted +import gov.nasa.ammos.aerie.procedural.timeline.util.truncateList import java.util.function.Consumer /** @@ -82,8 +82,8 @@ interface GeneralOps, THIS: GeneralOps>: Timeline, THIS: ParallelOps>: GeneralOp * @param unit base unit of time to count. As in, the resulting real profile will increase by * `1` for each `unit` duration spent in a payload object. * - * @see gov.nasa.jpl.aerie.timeline.ops.numeric.SerialNumericOps.integrate for further explanation of [unit]. + * @see gov.nasa.ammos.aerie.procedural.timeline.ops.numeric.SerialNumericOps.integrate for further explanation of [unit]. */ fun accumulatedDuration(unit: Duration) = countActive().integrate(unit) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SegmentOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SegmentOps.kt similarity index 92% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SegmentOps.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SegmentOps.kt index 36a99cf1e3..c6895730ac 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SegmentOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SegmentOps.kt @@ -1,9 +1,9 @@ -package gov.nasa.jpl.aerie.timeline.ops +package gov.nasa.ammos.aerie.procedural.timeline.ops -import gov.nasa.jpl.aerie.timeline.* -import gov.nasa.jpl.aerie.timeline.BoundsTransformer.Companion.IDENTITY -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.util.map2ParallelLists +import gov.nasa.ammos.aerie.procedural.timeline.* +import gov.nasa.ammos.aerie.procedural.timeline.BoundsTransformer.Companion.IDENTITY +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.util.map2ParallelLists /** * Operations mixin for timelines of segments. @@ -89,7 +89,7 @@ interface SegmentOps>: NonZeroDurationOps, THIS: SerialOps>: GeneralOps { diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialSegmentOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOps.kt similarity index 90% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialSegmentOps.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOps.kt index a19641dc3a..9ad331c325 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialSegmentOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOps.kt @@ -1,15 +1,15 @@ -package gov.nasa.jpl.aerie.timeline.ops +package gov.nasa.ammos.aerie.procedural.timeline.ops -import gov.nasa.jpl.aerie.timeline.* -import gov.nasa.jpl.aerie.timeline.Interval.Companion.at -import gov.nasa.jpl.aerie.timeline.collections.profiles.Booleans -import gov.nasa.jpl.aerie.timeline.collections.profiles.Constants -import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegmentsOp -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.payloads.transpose -import gov.nasa.jpl.aerie.timeline.util.coalesceList -import gov.nasa.jpl.aerie.timeline.util.map2SegmentLists -import gov.nasa.jpl.aerie.timeline.util.truncateList +import gov.nasa.ammos.aerie.procedural.timeline.* +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.at +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Constants +import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceSegmentsOp +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.payloads.transpose +import gov.nasa.ammos.aerie.procedural.timeline.util.coalesceList +import gov.nasa.ammos.aerie.procedural.timeline.util.map2SegmentLists +import gov.nasa.ammos.aerie.procedural.timeline.util.truncateList /** * Operations mixin for timelines of ordered, non-overlapping segments (profiles). @@ -82,7 +82,7 @@ interface SerialSegmentOps>: SerialOps< * is then collected on the interval it corresponds to, and the results are concatenated into a single profile. * * This is useful for binary operations where at least one of the operand segments represents a value that - * varies within the segment, such as [gov.nasa.jpl.aerie.timeline.collections.profiles.Real]. + * varies within the segment, such as [gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real]. * * @param W the other operand's payload type * @param R the result's payload type diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/coalesce/CoalesceIntervalsOp.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/coalesce/CoalesceIntervalsOp.kt new file mode 100644 index 0000000000..dbe114594c --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/coalesce/CoalesceIntervalsOp.kt @@ -0,0 +1,10 @@ +package gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce + +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.ops.GeneralOps + +/** A coalesce operation for intervals, which always coalesce. */ +interface CoalesceIntervalsOp>: GeneralOps { + override fun shouldCoalesce() = { _: Interval, _: Interval -> true } +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/coalesce/CoalesceNoOp.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/coalesce/CoalesceNoOp.kt new file mode 100644 index 0000000000..f535ba8baa --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/coalesce/CoalesceNoOp.kt @@ -0,0 +1,9 @@ +package gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce + +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.ops.GeneralOps + +/** A no-op implementation of coalesce. */ +interface CoalesceNoOp, THIS: CoalesceNoOp>: GeneralOps { + override fun shouldCoalesce() = null +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/coalesce/CoalesceSegmentsOp.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/coalesce/CoalesceSegmentsOp.kt similarity index 52% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/coalesce/CoalesceSegmentsOp.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/coalesce/CoalesceSegmentsOp.kt index fb8b95f8c9..aa8a078ceb 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/coalesce/CoalesceSegmentsOp.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/coalesce/CoalesceSegmentsOp.kt @@ -1,7 +1,7 @@ -package gov.nasa.jpl.aerie.timeline.ops.coalesce +package gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.ops.GeneralOps +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.ops.GeneralOps /** Implements coalescing for segments. */ interface CoalesceSegmentsOp>: GeneralOps, THIS> { diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/LinearOps.kt similarity index 81% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOps.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/LinearOps.kt index e77374f17b..3e022cc480 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/LinearOps.kt @@ -1,10 +1,10 @@ -package gov.nasa.jpl.aerie.timeline.ops.numeric +package gov.nasa.ammos.aerie.procedural.timeline.ops.numeric import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.timeline.BoundsTransformer -import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation +import gov.nasa.ammos.aerie.procedural.timeline.BoundsTransformer +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation /** * Operations mixin for segment-valued timelines whose payloads diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/NumericOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/NumericOps.kt similarity index 72% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/NumericOps.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/NumericOps.kt index 0716e4d1d4..06f09c4fdc 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/NumericOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/NumericOps.kt @@ -1,7 +1,7 @@ -package gov.nasa.jpl.aerie.timeline.ops.numeric +package gov.nasa.ammos.aerie.procedural.timeline.ops.numeric -import gov.nasa.jpl.aerie.timeline.ops.SegmentOps -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation +import gov.nasa.ammos.aerie.procedural.timeline.ops.SegmentOps +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation /** Operations for all timelines of segments that represent numbers. */ interface NumericOps>: SegmentOps { diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/PrimitiveNumberOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/PrimitiveNumberOps.kt similarity index 88% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/PrimitiveNumberOps.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/PrimitiveNumberOps.kt index 90ea537773..45b7e94b4c 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/PrimitiveNumberOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/PrimitiveNumberOps.kt @@ -1,8 +1,8 @@ -package gov.nasa.jpl.aerie.timeline.ops.numeric +package gov.nasa.ammos.aerie.procedural.timeline.ops.numeric -import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers -import gov.nasa.jpl.aerie.timeline.ops.ConstantOps -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers +import gov.nasa.ammos.aerie.procedural.timeline.ops.ConstantOps +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation import kotlin.math.absoluteValue /** Operations for timelines of primitive numbers. */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/SerialNumericOps.kt similarity index 84% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOps.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/SerialNumericOps.kt index e5488433a5..3e94095b9f 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/SerialNumericOps.kt @@ -1,11 +1,11 @@ -package gov.nasa.jpl.aerie.timeline.ops.numeric +package gov.nasa.ammos.aerie.procedural.timeline.ops.numeric import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.collections.profiles.Real -import gov.nasa.jpl.aerie.timeline.ops.SerialSegmentOps -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real +import gov.nasa.ammos.aerie.procedural.timeline.ops.SerialSegmentOps +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation /** diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/Connection.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/Connection.kt similarity index 79% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/Connection.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/Connection.kt index 0b68a096b5..f530dfbc7a 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/Connection.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/Connection.kt @@ -1,6 +1,6 @@ -package gov.nasa.jpl.aerie.timeline.payloads +package gov.nasa.ammos.aerie.procedural.timeline.payloads -import gov.nasa.jpl.aerie.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.Interval /** Represents a pairing of two other [IntervalLike] objects. */ data class Connection, TO: IntervalLike>( diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/IntervalLike.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/IntervalLike.kt similarity index 79% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/IntervalLike.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/IntervalLike.kt index b6e120e1ec..bb92887815 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/IntervalLike.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/IntervalLike.kt @@ -1,6 +1,6 @@ -package gov.nasa.jpl.aerie.timeline.payloads +package gov.nasa.ammos.aerie.procedural.timeline.payloads -import gov.nasa.jpl.aerie.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.Interval /** * An interface for objects that have a interval-like presence on a timeline. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/LinearEquation.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/LinearEquation.kt similarity index 92% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/LinearEquation.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/LinearEquation.kt index 21a55ba243..7a63558a08 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/LinearEquation.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/LinearEquation.kt @@ -1,12 +1,12 @@ -package gov.nasa.jpl.aerie.timeline.payloads +package gov.nasa.ammos.aerie.procedural.timeline.payloads import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive -import gov.nasa.jpl.aerie.timeline.collections.profiles.Real -import gov.nasa.jpl.aerie.timeline.collections.profiles.Booleans -import gov.nasa.jpl.aerie.timeline.util.duration.div +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Exclusive +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Inclusive +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.div import java.util.function.BiFunction import kotlin.math.abs import kotlin.math.absoluteValue diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/Segment.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/Segment.kt similarity index 91% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/Segment.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/Segment.kt index 0d12b1afc1..40f0bba81e 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/Segment.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/Segment.kt @@ -1,7 +1,7 @@ -package gov.nasa.jpl.aerie.timeline.payloads +package gov.nasa.ammos.aerie.procedural.timeline.payloads -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.util.coalesceList +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.util.coalesceList /** * A generic container that associates a value with an interval on a timeline. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Activity.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Activity.kt similarity index 69% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Activity.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Activity.kt index 1a54cfc946..120c7a6464 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Activity.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Activity.kt @@ -1,7 +1,7 @@ -package gov.nasa.jpl.aerie.timeline.payloads.activities +package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike /** Unifying interface for activity instances and directives. */ interface Activity>: IntervalLike
{ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyDirective.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyDirective.kt similarity index 90% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyDirective.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyDirective.kt index bd62215846..a5b13a1d4d 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyDirective.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyDirective.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.timeline.payloads.activities +package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import kotlin.jvm.optionals.getOrNull diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyInstance.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyInstance.kt similarity index 94% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyInstance.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyInstance.kt index bf951053b8..d91d7f1ed2 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/AnyInstance.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyInstance.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.timeline.payloads.activities +package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import kotlin.jvm.optionals.getOrNull diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt similarity index 90% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt index 5f19675fa9..f1af0ea611 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Directive.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt @@ -1,7 +1,7 @@ -package gov.nasa.jpl.aerie.timeline.payloads.activities +package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.Interval /** A wrapper of any type of activity directive containing common data. */ data class Directive( diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/DirectiveStart.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/DirectiveStart.kt similarity index 95% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/DirectiveStart.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/DirectiveStart.kt index 5c04e150aa..375f16fc88 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/DirectiveStart.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/DirectiveStart.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.timeline.payloads.activities +package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities import gov.nasa.jpl.aerie.merlin.protocol.types.Duration diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Instance.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt similarity index 86% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Instance.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt index 59ec8c298c..183275412e 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/payloads/activities/Instance.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt @@ -1,7 +1,7 @@ -package gov.nasa.jpl.aerie.timeline.payloads.activities +package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.Interval /** A wrapper of any type of activity instance containing common data. */ data class Instance( diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/Plan.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/Plan.kt similarity index 82% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/Plan.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/Plan.kt index d6e5ec17a3..4ef6ef1b50 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/Plan.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/Plan.kt @@ -1,10 +1,10 @@ -package gov.nasa.jpl.aerie.timeline.plan +package gov.nasa.ammos.aerie.procedural.timeline.plan import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyDirective -import gov.nasa.jpl.aerie.timeline.collections.Directives +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective +import gov.nasa.ammos.aerie.procedural.timeline.collections.Directives import java.time.Instant /** An interface for querying plan information and simulation results. */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulatedPlan.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/SimulatedPlan.kt similarity index 80% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulatedPlan.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/SimulatedPlan.kt index 2b251428a9..cf23624821 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulatedPlan.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/SimulatedPlan.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.timeline.plan +package gov.nasa.ammos.aerie.procedural.timeline.plan /** A connection to Aerie's database for a particular simulation result. */ data class SimulatedPlan( diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulationResults.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/SimulationResults.kt similarity index 76% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulationResults.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/SimulationResults.kt index 0dc05c2f08..561ebcb628 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/plan/SimulationResults.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/SimulationResults.kt @@ -1,11 +1,11 @@ -package gov.nasa.jpl.aerie.timeline.plan +package gov.nasa.ammos.aerie.procedural.timeline.plan import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.payloads.activities.AnyInstance -import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegmentsOp -import gov.nasa.jpl.aerie.timeline.collections.Instances +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyInstance +import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceSegmentsOp +import gov.nasa.ammos.aerie.procedural.timeline.collections.Instances /** An interface for querying plan information and simulation results. */ interface SimulationResults { diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/Coalesce.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Coalesce.kt similarity index 93% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/Coalesce.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Coalesce.kt index e370346563..5d2df96298 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/Coalesce.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Coalesce.kt @@ -1,7 +1,7 @@ -package gov.nasa.jpl.aerie.timeline.util +package gov.nasa.ammos.aerie.procedural.timeline.util -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike /** Coalesces a list if the provided [shouldCoalesce] function is not `null`. */ fun > maybeCoalesce(list: List, shouldCoalesce: (I.(I) -> Boolean)?) = diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/ListUtils.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListUtils.kt similarity index 89% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/ListUtils.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListUtils.kt index 189f7f2967..4adf2bb73e 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/ListUtils.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListUtils.kt @@ -1,8 +1,8 @@ -package gov.nasa.jpl.aerie.timeline.util +package gov.nasa.ammos.aerie.procedural.timeline.util -import gov.nasa.jpl.aerie.timeline.CollectOptions -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike /** * Sanitizes a list of [IntervalLike] objects for use in a timeline. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Map2.kt similarity index 95% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Map2.kt index adc8f1e17d..7c48eebe46 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Map2.kt @@ -1,10 +1,10 @@ -package gov.nasa.jpl.aerie.timeline.util +package gov.nasa.ammos.aerie.procedural.timeline.util -import gov.nasa.jpl.aerie.timeline.NullBinaryOperation -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.payloads.transpose +import gov.nasa.ammos.aerie.procedural.timeline.NullBinaryOperation +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.payloads.transpose /** * Low level routine for performing a binary operation on a pair of segment lists. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/duration/Duration.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/duration/Duration.kt similarity index 93% rename from procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/duration/Duration.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/duration/Duration.kt index 01709734ca..44ff8df369 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/util/duration/Duration.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/duration/Duration.kt @@ -1,8 +1,8 @@ -package gov.nasa.jpl.aerie.timeline.util.duration +package gov.nasa.ammos.aerie.procedural.timeline.util.duration import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.* -import gov.nasa.jpl.aerie.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.Interval import java.time.Instant import java.time.temporal.ChronoUnit diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Intervals.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Intervals.kt deleted file mode 100644 index a0dba2cd53..0000000000 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/collections/Intervals.kt +++ /dev/null @@ -1,19 +0,0 @@ -package gov.nasa.jpl.aerie.timeline.collections - -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike -import gov.nasa.jpl.aerie.timeline.BaseTimeline -import gov.nasa.jpl.aerie.timeline.ops.ParallelOps -import gov.nasa.jpl.aerie.timeline.Timeline -import gov.nasa.jpl.aerie.timeline.ops.NonZeroDurationOps -import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceNoOp -import gov.nasa.jpl.aerie.timeline.util.preprocessList - -/** A timeline of any [IntervalLike] object with no special operations. */ -data class Intervals>(private val timeline: Timeline>): - Timeline> by timeline, - ParallelOps>, - NonZeroDurationOps> -{ - constructor(vararg intervals: T): this(intervals.asList()) - constructor(intervals: List): this(BaseTimeline(::Intervals, preprocessList(intervals, null))) -} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/coalesce/CoalesceIntervalsOp.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/coalesce/CoalesceIntervalsOp.kt deleted file mode 100644 index f3e6bacb4c..0000000000 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/coalesce/CoalesceIntervalsOp.kt +++ /dev/null @@ -1,10 +0,0 @@ -package gov.nasa.jpl.aerie.timeline.ops.coalesce - -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike -import gov.nasa.jpl.aerie.timeline.ops.GeneralOps - -/** A coalesce operation for intervals, which always coalesce. */ -interface CoalesceIntervalsOp>: GeneralOps { - override fun shouldCoalesce() = { _: Interval, _: Interval -> true } -} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/coalesce/CoalesceNoOp.kt b/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/coalesce/CoalesceNoOp.kt deleted file mode 100644 index ed89ef2456..0000000000 --- a/procedural/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/coalesce/CoalesceNoOp.kt +++ /dev/null @@ -1,9 +0,0 @@ -package gov.nasa.jpl.aerie.timeline.ops.coalesce - -import gov.nasa.jpl.aerie.timeline.payloads.IntervalLike -import gov.nasa.jpl.aerie.timeline.ops.GeneralOps - -/** A no-op implementation of coalesce. */ -interface CoalesceNoOp, THIS: CoalesceNoOp>: GeneralOps { - override fun shouldCoalesce() = null -} diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformerTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BoundsTransformerTest.kt similarity index 75% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformerTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BoundsTransformerTest.kt index 1a8c158b5a..e6da888551 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/BoundsTransformerTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BoundsTransformerTest.kt @@ -1,7 +1,7 @@ -package gov.nasa.jpl.aerie.timeline +package gov.nasa.ammos.aerie.procedural.timeline import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/IntervalTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/IntervalTest.kt similarity index 91% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/IntervalTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/IntervalTest.kt index 3184c7d4d8..64b8292eff 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/IntervalTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/IntervalTest.kt @@ -1,14 +1,14 @@ -package gov.nasa.jpl.aerie.timeline +package gov.nasa.ammos.aerie.procedural.timeline import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.Interval.Companion.EMPTY -import gov.nasa.jpl.aerie.timeline.Interval.Companion.at -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Companion.betweenClosedOpen -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.* -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo -import gov.nasa.jpl.aerie.timeline.util.duration.rangeUntil +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.EMPTY +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.at +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.betweenClosedOpen +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.* +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeUntil import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/SegmentTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/SegmentTest.kt similarity index 59% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/SegmentTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/SegmentTest.kt index db19e54d6a..c2fb8c4660 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/SegmentTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/SegmentTest.kt @@ -1,9 +1,9 @@ -package gov.nasa.jpl.aerie.timeline +package gov.nasa.ammos.aerie.procedural.timeline import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.Interval.Companion.at -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.payloads.transpose +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.at +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.payloads.transpose import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/WindowsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/WindowsTest.kt similarity index 85% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/WindowsTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/WindowsTest.kt index 1bbbf582ff..c26b56de17 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/WindowsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/WindowsTest.kt @@ -1,14 +1,14 @@ -package gov.nasa.jpl.aerie.timeline.collections +package gov.nasa.ammos.aerie.procedural.timeline.collections -import gov.nasa.jpl.aerie.timeline.CollectOptions +import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.milliseconds import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.Interval.Companion.at -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo -import gov.nasa.jpl.aerie.timeline.util.duration.rangeUntil +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.at +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Exclusive +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeUntil import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/BooleansTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/BooleansTest.kt similarity index 82% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/BooleansTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/BooleansTest.kt index baf4802982..d2c37c0573 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/BooleansTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/BooleansTest.kt @@ -1,11 +1,11 @@ -package gov.nasa.jpl.aerie.timeline.collections.profiles +package gov.nasa.ammos.aerie.procedural.timeline.collections.profiles import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.CollectOptions -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo -import gov.nasa.jpl.aerie.timeline.util.duration.rangeUntil +import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeUntil import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/NumbersTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/NumbersTest.kt similarity index 70% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/NumbersTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/NumbersTest.kt index 2e67a83cad..7497c99e31 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/NumbersTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/NumbersTest.kt @@ -1,10 +1,10 @@ -package gov.nasa.jpl.aerie.timeline.collections.profiles +package gov.nasa.ammos.aerie.procedural.timeline.collections.profiles import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/RealTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/RealTest.kt similarity index 67% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/RealTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/RealTest.kt index 0653a7e9fe..b7d85bb698 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/collections/profiles/RealTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/RealTest.kt @@ -1,10 +1,10 @@ -package gov.nasa.jpl.aerie.timeline.collections.profiles +package gov.nasa.ammos.aerie.procedural.timeline.collections.profiles import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation -import gov.nasa.jpl.aerie.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment import org.junit.jupiter.api.Assertions.assertIterableEquals -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo import org.junit.jupiter.api.Test class RealTest { diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOpsTest.kt similarity index 84% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOpsTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOpsTest.kt index 936315a2b4..4ca05bebfc 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/GeneralOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOpsTest.kt @@ -1,21 +1,21 @@ -package gov.nasa.jpl.aerie.timeline.ops +package gov.nasa.ammos.aerie.procedural.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.milliseconds import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.* -import gov.nasa.jpl.aerie.timeline.Interval.Companion.at -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Companion.betweenClosedOpen -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive -import gov.nasa.jpl.aerie.timeline.collections.Intervals -import gov.nasa.jpl.aerie.timeline.collections.profiles.Constants -import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers -import gov.nasa.jpl.aerie.timeline.collections.profiles.Booleans -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.util.duration.div -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.* +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.at +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.betweenClosedOpen +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Exclusive +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Inclusive +import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Constants +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.div +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/NonZeroDurationOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/NonZeroDurationOpsTest.kt similarity index 76% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/NonZeroDurationOpsTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/NonZeroDurationOpsTest.kt index e8d95fe3d3..ac1275ca90 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/NonZeroDurationOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/NonZeroDurationOpsTest.kt @@ -1,13 +1,13 @@ -package gov.nasa.jpl.aerie.timeline.ops +package gov.nasa.ammos.aerie.procedural.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.CollectOptions -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Exclusive +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Inclusive +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/ParallelOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOpsTest.kt similarity index 67% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/ParallelOpsTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOpsTest.kt index 8ac29a84f3..b9e72aeae3 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/ParallelOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOpsTest.kt @@ -1,16 +1,16 @@ -package gov.nasa.jpl.aerie.timeline.ops +package gov.nasa.ammos.aerie.procedural.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.NullBinaryOperation -import gov.nasa.jpl.aerie.timeline.Interval.Companion.at -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Companion.betweenClosedOpen -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.collections.Intervals -import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.NullBinaryOperation +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.at +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.betweenClosedOpen +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Exclusive +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Inclusive +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialConstantTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialConstantTest.kt similarity index 65% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialConstantTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialConstantTest.kt index 6983c4961a..7c9b520b94 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialConstantTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialConstantTest.kt @@ -1,13 +1,13 @@ -package gov.nasa.jpl.aerie.timeline.ops +package gov.nasa.ammos.aerie.procedural.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.Interval.Companion.at -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.at +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Exclusive +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Inclusive +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialSegmentOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOpsTest.kt similarity index 68% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialSegmentOpsTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOpsTest.kt index 4e8c86376a..6768dc1c0e 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialSegmentOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOpsTest.kt @@ -1,16 +1,16 @@ -package gov.nasa.jpl.aerie.timeline.ops +package gov.nasa.ammos.aerie.procedural.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.milliseconds import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.NullBinaryOperation -import gov.nasa.jpl.aerie.timeline.Interval.Companion.at -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Companion.betweenClosedOpen -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Exclusive -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.Inclusive -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.collections.profiles.Constants -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.NullBinaryOperation +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.at +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.betweenClosedOpen +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Exclusive +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Inclusive +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Constants +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/LinearOpsTest.kt similarity index 58% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOpsTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/LinearOpsTest.kt index 0920366bcb..5a5d9b01d7 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/LinearOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/LinearOpsTest.kt @@ -1,10 +1,10 @@ -package gov.nasa.jpl.aerie.timeline.ops.numeric +package gov.nasa.ammos.aerie.procedural.timeline.ops.numeric import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.collections.profiles.Real -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/PrimitiveNumberOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/PrimitiveNumberOpsTest.kt similarity index 81% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/PrimitiveNumberOpsTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/PrimitiveNumberOpsTest.kt index 1ccc20d84f..4de45f0fac 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/PrimitiveNumberOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/PrimitiveNumberOpsTest.kt @@ -1,9 +1,9 @@ -package gov.nasa.jpl.aerie.timeline.ops.numeric +package gov.nasa.ammos.aerie.procedural.timeline.ops.numeric import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.Interval.Companion.at -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.at +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/SerialNumericOpsTest.kt similarity index 79% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOpsTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/SerialNumericOpsTest.kt index 92f0a4bb9f..b107211166 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/numeric/SerialNumericOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/SerialNumericOpsTest.kt @@ -1,14 +1,14 @@ -package gov.nasa.jpl.aerie.timeline.ops.numeric +package gov.nasa.ammos.aerie.procedural.timeline.ops.numeric import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.collections.profiles.Numbers -import gov.nasa.jpl.aerie.timeline.collections.profiles.Real -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo -import gov.nasa.jpl.aerie.timeline.util.duration.rangeUntil +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeUntil import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/CoalesceTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/CoalesceTest.kt similarity index 70% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/CoalesceTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/CoalesceTest.kt index 551da292c8..4c6db6b675 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/CoalesceTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/CoalesceTest.kt @@ -1,11 +1,11 @@ -package gov.nasa.jpl.aerie.timeline.util +package gov.nasa.ammos.aerie.procedural.timeline.util import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.Interval.Companion.at -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.* -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.at +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.* +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/LinearEquationTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/LinearEquationTest.kt similarity index 81% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/LinearEquationTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/LinearEquationTest.kt index 0956734f82..d9908cbee7 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/LinearEquationTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/LinearEquationTest.kt @@ -1,13 +1,13 @@ -package gov.nasa.jpl.aerie.timeline.util +package gov.nasa.ammos.aerie.procedural.timeline.util import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Companion.betweenClosedOpen -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.collections.profiles.Real -import gov.nasa.jpl.aerie.timeline.collections.profiles.Booleans -import gov.nasa.jpl.aerie.timeline.payloads.LinearEquation +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.betweenClosedOpen +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/ListCollectorTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListCollectorTest.kt similarity index 83% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/ListCollectorTest.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListCollectorTest.kt index baa1e8ee08..bf2ccccf82 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/ListCollectorTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListCollectorTest.kt @@ -1,12 +1,12 @@ -package gov.nasa.jpl.aerie.timeline.util +package gov.nasa.ammos.aerie.procedural.timeline.util import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.CollectOptions +import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.collections.Intervals -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo val bounds = (seconds(0) .. seconds(10)) diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2Test.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Map2Test.kt similarity index 92% rename from procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2Test.kt rename to procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Map2Test.kt index 93f337f89d..465d082959 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/util/Map2Test.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Map2Test.kt @@ -1,13 +1,13 @@ -package gov.nasa.jpl.aerie.timeline.util +package gov.nasa.ammos.aerie.procedural.timeline.util import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds -import gov.nasa.jpl.aerie.timeline.NullBinaryOperation -import gov.nasa.jpl.aerie.timeline.Interval -import gov.nasa.jpl.aerie.timeline.Interval.Companion.between -import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.* -import gov.nasa.jpl.aerie.timeline.payloads.Segment -import gov.nasa.jpl.aerie.timeline.util.duration.rangeTo -import gov.nasa.jpl.aerie.timeline.util.duration.rangeUntil +import gov.nasa.ammos.aerie.procedural.timeline.NullBinaryOperation +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between +import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.* +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeUntil import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* From 7d366e4fce772334eb3ecd0d81fbcb84bb264262 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Tue, 6 Aug 2024 15:41:34 -0700 Subject: [PATCH 012/108] Move remote package from jpl to ammos --- .../test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java | 4 ++-- procedural/remote/MODULE_DOCS.md | 2 +- .../aerie/procedural/remote/AeriePostgresPlan.kt | 2 +- .../aerie/procedural/remote/AeriePostgresSimulationResults.kt | 2 +- .../nasa/{jpl => ammos}/aerie/procedural/remote/SqlUtils.kt | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename procedural/remote/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/remote/AeriePostgresPlan.kt (99%) rename procedural/remote/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/remote/AeriePostgresSimulationResults.kt (99%) rename procedural/remote/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/remote/SqlUtils.kt (93%) diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java index 99222c7a37..20faa10256 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java @@ -10,8 +10,8 @@ import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real; import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation; import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment; -import gov.nasa.jpl.aerie.procedural.remote.AeriePostgresPlan; -import gov.nasa.jpl.aerie.procedural.remote.AeriePostgresSimulationResults; +import gov.nasa.ammos.aerie.procedural.remote.AeriePostgresPlan; +import gov.nasa.ammos.aerie.procedural.remote.AeriePostgresSimulationResults; import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulatedPlan; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; diff --git a/procedural/remote/MODULE_DOCS.md b/procedural/remote/MODULE_DOCS.md index 9eb37ad2bb..fe3e3e0f53 100644 --- a/procedural/remote/MODULE_DOCS.md +++ b/procedural/remote/MODULE_DOCS.md @@ -4,5 +4,5 @@ This library provides tools for accessing plans and simulation results from remo This is intended to allow goal and constraint authors to run their code outside Aerie, and these implementations do not need to be packaged in the goal and constraint jars. -# Package gov.nasa.jpl.aerie.procedural.remote +# Package gov.nasa.ammos.aerie.procedural.remote Remote utilities for running goals and constraints outside of Aerie. diff --git a/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresPlan.kt b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresPlan.kt similarity index 99% rename from procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresPlan.kt rename to procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresPlan.kt index 2f02617ffc..b949a8c15d 100644 --- a/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresPlan.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresPlan.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.remote +package gov.nasa.ammos.aerie.procedural.remote import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser.serializedValueP import gov.nasa.jpl.aerie.merlin.protocol.types.Duration diff --git a/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresSimulationResults.kt b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt similarity index 99% rename from procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresSimulationResults.kt rename to procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt index 717266035e..95eda666f5 100644 --- a/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/AeriePostgresSimulationResults.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.remote +package gov.nasa.ammos.aerie.procedural.remote import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser.serializedValueP import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue diff --git a/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/SqlUtils.kt b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/SqlUtils.kt similarity index 93% rename from procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/SqlUtils.kt rename to procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/SqlUtils.kt index df5a052d4a..e941e90a1a 100644 --- a/procedural/remote/src/main/kotlin/gov/nasa/jpl/aerie/procedural/remote/SqlUtils.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/SqlUtils.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.remote +package gov.nasa.ammos.aerie.procedural.remote import java.sql.Connection import java.sql.PreparedStatement From aa9d38d0ff292c4e14f3ac238df5bc246442ee30 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Tue, 6 Aug 2024 15:43:09 -0700 Subject: [PATCH 013/108] Move procedural constraints from jpl to ammos --- .../aerie/procedural/constraints/ActivityId.kt | 2 +- .../aerie/procedural/constraints/Constraint.kt | 2 +- .../aerie/procedural/constraints/Violation.kt | 2 +- .../aerie/procedural/constraints/Violations.kt | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) rename procedural/constraints/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/constraints/ActivityId.kt (81%) rename procedural/constraints/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/constraints/Constraint.kt (93%) rename procedural/constraints/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/constraints/Violation.kt (94%) rename procedural/constraints/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/constraints/Violations.kt (94%) diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/ActivityId.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/ActivityId.kt similarity index 81% rename from procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/ActivityId.kt rename to procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/ActivityId.kt index 337df89065..0554d88556 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/ActivityId.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/ActivityId.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.constraints +package gov.nasa.ammos.aerie.procedural.constraints /** An activity ID, referencing either a directive or instance. */ sealed interface ActivityId { diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Constraint.kt similarity index 93% rename from procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt rename to procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Constraint.kt index c6dd41e8c1..e08f5c2aa2 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Constraint.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Constraint.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.constraints +package gov.nasa.ammos.aerie.procedural.constraints import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulatedPlan diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violation.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violation.kt similarity index 94% rename from procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violation.kt rename to procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violation.kt index 7dc7b81fc6..8a396416a4 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violation.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violation.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.constraints +package gov.nasa.ammos.aerie.procedural.constraints import gov.nasa.ammos.aerie.procedural.timeline.Interval import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violations.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt similarity index 94% rename from procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violations.kt rename to procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt index 1f9378e926..bd93ae5de6 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/jpl/aerie/procedural/constraints/Violations.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt @@ -1,7 +1,7 @@ -package gov.nasa.jpl.aerie.procedural.constraints +package gov.nasa.ammos.aerie.procedural.constraints -import gov.nasa.jpl.aerie.procedural.constraints.ActivityId.DirectiveId -import gov.nasa.jpl.aerie.procedural.constraints.ActivityId.InstanceId +import gov.nasa.ammos.aerie.procedural.constraints.ActivityId.DirectiveId +import gov.nasa.ammos.aerie.procedural.constraints.ActivityId.InstanceId import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline import gov.nasa.ammos.aerie.procedural.timeline.BoundsTransformer import gov.nasa.ammos.aerie.procedural.timeline.Timeline From 27f644ec888cc06c476f5c01532f4db92ed99239 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Tue, 6 Aug 2024 15:51:45 -0700 Subject: [PATCH 014/108] Move procedural scheduling from jpl to ammos package --- procedural/scheduling/MODULE_DOCS.md | 8 ++++---- .../{jpl => ammos}/aerie/procedural/scheduling/Rule.kt | 4 ++-- .../aerie/procedural/scheduling/plan/Edit.kt | 2 +- .../aerie/procedural/scheduling/plan/EditablePlan.kt | 4 ++-- .../aerie/procedural/scheduling/plan/NewDirective.kt | 2 +- .../scheduling/simulation/CheckpointGeneration.kt | 2 +- .../scheduling/simulation/CheckpointRetention.kt | 2 +- .../procedural/scheduling/simulation/PauseBehavior.kt | 2 +- .../procedural/scheduling/simulation/SimulateOptions.kt | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) rename procedural/scheduling/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/scheduling/Rule.kt (65%) rename procedural/scheduling/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/scheduling/plan/Edit.kt (88%) rename procedural/scheduling/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/scheduling/plan/EditablePlan.kt (92%) rename procedural/scheduling/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/scheduling/plan/NewDirective.kt (95%) rename procedural/scheduling/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt (91%) rename procedural/scheduling/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/scheduling/simulation/CheckpointRetention.kt (91%) rename procedural/scheduling/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/scheduling/simulation/PauseBehavior.kt (94%) rename procedural/scheduling/src/main/kotlin/gov/nasa/{jpl => ammos}/aerie/procedural/scheduling/simulation/SimulateOptions.kt (88%) diff --git a/procedural/scheduling/MODULE_DOCS.md b/procedural/scheduling/MODULE_DOCS.md index 9decc70e6f..6279088d2c 100644 --- a/procedural/scheduling/MODULE_DOCS.md +++ b/procedural/scheduling/MODULE_DOCS.md @@ -5,17 +5,17 @@ To write a procedure, create a class that implements the Procedure interface. Then, follow the tutorial documentation (TODO: link to tutorial documentation once written) to package and upload your constraint to Aerie. -# Package gov.nasa.jpl.aerie.procedural.scheduling +# Package gov.nasa.ammos.aerie.procedural.scheduling The top-level procedure types. -# Package gov.nasa.jpl.aerie.procedural.scheduling.plan +# Package gov.nasa.ammos.aerie.procedural.scheduling.plan Contains the EditablePlan interface that procedure execution environments need to implement. -# Package gov.nasa.jpl.aerie.procedural.scheduling.simulation +# Package gov.nasa.ammos.aerie.procedural.scheduling.simulation Contains configuration options for spawning simulations. -# Package gov.nasa.jpl.aerie.procedural.scheduling.annotations +# Package gov.nasa.ammos.aerie.procedural.scheduling.annotations Annotations for serializing and deserializing procedure arguments. diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Rule.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/Rule.kt similarity index 65% rename from procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Rule.kt rename to procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/Rule.kt index 3f665fb2cf..4332e3f12d 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/Rule.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/Rule.kt @@ -1,6 +1,6 @@ -package gov.nasa.jpl.aerie.procedural.scheduling +package gov.nasa.ammos.aerie.procedural.scheduling -import gov.nasa.jpl.aerie.procedural.scheduling.plan.EditablePlan +import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan /** The interface that all scheduling rules must satisfy. */ interface Rule { diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/Edit.kt similarity index 88% rename from procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt rename to procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/Edit.kt index b33081ac04..2edcca9fb5 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/Edit.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/Edit.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.scheduling.plan +package gov.nasa.ammos.aerie.procedural.scheduling.plan import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/EditablePlan.kt similarity index 92% rename from procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt rename to procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/EditablePlan.kt index 1c35ef1d51..1d6f6b002c 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/EditablePlan.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/EditablePlan.kt @@ -1,7 +1,7 @@ -package gov.nasa.jpl.aerie.procedural.scheduling.plan +package gov.nasa.ammos.aerie.procedural.scheduling.plan import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.procedural.scheduling.simulation.SimulateOptions +import gov.nasa.ammos.aerie.procedural.scheduling.simulation.SimulateOptions import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt similarity index 95% rename from procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt rename to procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt index ffbd29d7e8..868e38ce37 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/plan/NewDirective.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.scheduling.plan +package gov.nasa.ammos.aerie.procedural.scheduling.plan import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt similarity index 91% rename from procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt rename to procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt index 52e95e8b38..5fcecda825 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/CheckpointGeneration.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.scheduling.simulation +package gov.nasa.ammos.aerie.procedural.scheduling.simulation import gov.nasa.jpl.aerie.merlin.protocol.types.Duration diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/CheckpointRetention.kt similarity index 91% rename from procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt rename to procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/CheckpointRetention.kt index 10ce6a4d13..7c9e0ba808 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/CheckpointRetention.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/CheckpointRetention.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.scheduling.simulation +package gov.nasa.ammos.aerie.procedural.scheduling.simulation import gov.nasa.jpl.aerie.merlin.protocol.types.Duration diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/PauseBehavior.kt similarity index 94% rename from procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt rename to procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/PauseBehavior.kt index dcea98cafd..ed436a5664 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/PauseBehavior.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/PauseBehavior.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.scheduling.simulation +package gov.nasa.ammos.aerie.procedural.scheduling.simulation import gov.nasa.jpl.aerie.merlin.protocol.types.Duration diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/SimulateOptions.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/SimulateOptions.kt similarity index 88% rename from procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/SimulateOptions.kt rename to procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/SimulateOptions.kt index 033ae0d245..96bf40b606 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/jpl/aerie/procedural/scheduling/simulation/SimulateOptions.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/SimulateOptions.kt @@ -1,4 +1,4 @@ -package gov.nasa.jpl.aerie.procedural.scheduling.simulation +package gov.nasa.ammos.aerie.procedural.scheduling.simulation /** Configuration for simulation. */ /* data */ class SimulateOptions( From 7112e0bddef1590c941973f9efa3d5f50f593bba Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Tue, 20 Aug 2024 11:36:49 -0700 Subject: [PATCH 015/108] Remove obsolete scheduling jar path --- .../jpl/aerie/scheduler/worker/SchedulerWorkerAppDriver.java | 2 -- .../jpl/aerie/scheduler/worker/WorkerAppConfiguration.java | 1 - .../scheduler/worker/services/SynchronousSchedulerAgent.java | 3 --- .../scheduler/worker/services/SchedulingIntegrationTests.java | 1 - 4 files changed, 7 deletions(-) diff --git a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/SchedulerWorkerAppDriver.java b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/SchedulerWorkerAppDriver.java index 9e1a35ef6e..274d0c297e 100644 --- a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/SchedulerWorkerAppDriver.java +++ b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/SchedulerWorkerAppDriver.java @@ -71,7 +71,6 @@ public static void main(String[] args) throws Exception { final var scheduleAgent = new SynchronousSchedulerAgent(specificationService, merlinDatabaseService, config.merlinFileStore(), - config.missionRuleJarPath(), config.outputMode(), schedulingDSLCompilationService); @@ -144,7 +143,6 @@ private static WorkerAppConfiguration loadConfiguration() { "aerie"), URI.create(getEnv("MERLIN_GRAPHQL_URL", "http://localhost:8080/v1/graphql")), Path.of(getEnv("MERLIN_LOCAL_STORE", "/usr/src/app/merlin_file_store")), - Path.of(getEnv("SCHEDULER_RULES_JAR", "/usr/src/app/merlin_file_store/scheduler_rules.jar")), PlanOutputMode.valueOf((getEnv("SCHEDULER_OUTPUT_MODE", "CreateNewOutputPlan"))), getEnv("HASURA_GRAPHQL_ADMIN_SECRET", ""), maxNbCachedSimulationEngine diff --git a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/WorkerAppConfiguration.java b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/WorkerAppConfiguration.java index 84b094cfc0..3390a01be4 100644 --- a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/WorkerAppConfiguration.java +++ b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/WorkerAppConfiguration.java @@ -9,7 +9,6 @@ public record WorkerAppConfiguration( Store store, URI merlinGraphqlURI, Path merlinFileStore, - Path missionRuleJarPath, PlanOutputMode outputMode, String hasuraGraphQlAdminSecret, int maxCachedSimulationEngines diff --git a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java index e9a68bf28c..54df92b893 100644 --- a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java +++ b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java @@ -80,7 +80,6 @@ * * @param merlinDatabaseService interface for querying plan and mission model details from merlin * @param modelJarsDir path to parent directory for mission model jars (interim backdoor jar file access) - * @param goalsJarPath path to jar file to load scheduling goals from (interim solution for user input goals) * @param outputMode how the scheduling output should be returned to aerie (eg overwrite or new container) */ //TODO: will eventually need scheduling goal service arg to pull goals from scheduler's own data store @@ -88,7 +87,6 @@ public record SynchronousSchedulerAgent( SpecificationService specificationService, MerlinDatabaseService.OwnerRole merlinDatabaseService, Path modelJarsDir, - Path goalsJarPath, PlanOutputMode outputMode, SchedulingDSLCompilationService schedulingDSLCompilationService ) @@ -99,7 +97,6 @@ public record SynchronousSchedulerAgent( public SynchronousSchedulerAgent { Objects.requireNonNull(merlinDatabaseService); Objects.requireNonNull(modelJarsDir); - Objects.requireNonNull(goalsJarPath); Objects.requireNonNull(schedulingDSLCompilationService); } diff --git a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingIntegrationTests.java b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingIntegrationTests.java index 3242e7ef39..49a7795655 100644 --- a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingIntegrationTests.java +++ b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingIntegrationTests.java @@ -2214,7 +2214,6 @@ private SchedulingRunResults runScheduler( specificationService, mockMerlinService, desc.libPath(), - Path.of(""), PlanOutputMode.UpdateInputPlanWithNewActivities, schedulingDSLCompiler); // Scheduling Goals -> Scheduling Specification From 791202ddafc31c3b8c067b517840507fb1d81572 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Tue, 6 Aug 2024 17:27:12 -0700 Subject: [PATCH 016/108] Update timeline library for procedural scheduling --- procedural/timeline/build.gradle | 23 +++ .../aerie/procedural/timeline/BaseTimeline.kt | 16 +- .../procedural/timeline/CollectOptions.kt | 7 + .../aerie/procedural/timeline/Timeline.kt | 17 ++ .../timeline/collections/profiles/Numbers.kt | 41 ++-- .../timeline/collections/profiles/Real.kt | 67 ++++--- .../timeline/collections/profiles/Strings.kt | 60 ++++++ .../procedural/timeline/ops/GeneralOps.kt | 6 +- .../timeline/ops/NonZeroDurationOps.kt | 2 +- .../procedural/timeline/ops/ParallelOps.kt | 6 +- .../timeline/ops/SerialConstantOps.kt | 11 ++ .../timeline/ops/SerialSegmentOps.kt | 2 +- .../timeline/ops/numeric/SerialNumericOps.kt | 15 ++ .../timeline/payloads/LinearEquation.kt | 2 +- .../procedural/timeline/util/ListUtils.kt | 182 ++++++++++++++++-- .../procedural/timeline/BaseTimelineTest.kt | 50 +++++ .../timeline/BoundsTransformerTest.kt | 2 +- .../timeline/collections/WindowsTest.kt | 32 +-- .../collections/profiles/BooleansTest.kt | 10 +- .../collections/profiles/NumbersTest.kt | 21 ++ .../timeline/collections/profiles/RealTest.kt | 38 ++++ .../collections/profiles/StringsTest.kt | 27 +++ .../timeline/ops/SerialConstantTest.kt | 22 +++ 23 files changed, 568 insertions(+), 91 deletions(-) create mode 100644 procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Strings.kt create mode 100644 procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimelineTest.kt create mode 100644 procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/StringsTest.kt diff --git a/procedural/timeline/build.gradle b/procedural/timeline/build.gradle index 63de0e53cb..d7329c3a4e 100644 --- a/procedural/timeline/build.gradle +++ b/procedural/timeline/build.gradle @@ -6,6 +6,7 @@ plugins { id "org.jetbrains.kotlin.jvm" version "1.9.22" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' + id 'maven-publish' } repositories { @@ -60,3 +61,25 @@ dokkaHtmlPartial.configure { } } } + +publishing { + publications { + library(MavenPublication) { + version = findProperty("publishing.version") + from components.java + } + } + + publishing { + repositories { + maven { + name = findProperty("publishing.name") + url = findProperty("publishing.url") + credentials { + username = System.getenv(findProperty("publishing.usernameEnvironmentVariable")) + password = System.getenv(findProperty("publishing.passwordEnvironmentVariable")) + } + } + } + } +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt index 3bfa0f181d..5f2fc9bf38 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt @@ -1,6 +1,7 @@ package gov.nasa.ammos.aerie.procedural.timeline import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike +import gov.nasa.ammos.aerie.procedural.timeline.util.listCollector /** * The basic timeline container that all higher-level timeline collections ultimately delegate to. @@ -11,7 +12,20 @@ data class BaseTimeline, TL: Timeline>( override val ctor: (Timeline) -> TL, private val collector: (CollectOptions) -> List ): Timeline { - override fun collect(opts: CollectOptions) = collector(opts) + private var cached: List? = null + private var cachedOptions: CollectOptions? = null + + override fun cache(opts: CollectOptions) { + if (cachedOptions == null || !cachedOptions!!.contains(opts)) { + cached = collect(opts) + cachedOptions = opts + } + } + + override fun collect(opts: CollectOptions) = + if (cached == null || !cachedOptions!!.contains(opts)) collector(opts) + else ctor(BaseTimeline(ctor, listCollector(cached!!))).collect(opts) + override fun > unsafeCast(ctor: (Timeline) -> RESULT) = BaseTimeline(ctor, collector).specialize() } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/CollectOptions.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/CollectOptions.kt index 16e62f6f34..e611a68124 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/CollectOptions.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/CollectOptions.kt @@ -16,4 +16,11 @@ data class CollectOptions @JvmOverloads constructor( ) { /** Creates a new options object with a [BoundsTransformer] applied. */ fun transformBounds(boundsTransformer: BoundsTransformer) = CollectOptions(boundsTransformer(bounds), truncateMarginal) + + /** + * Whether the results of collecting a timeline with [other] options are guaranteed to be contained in + * the results of collecting a timeline with these options. + */ + fun contains(other: CollectOptions) = bounds == Interval.MIN_MAX || + (bounds.contains(other.bounds) && (!truncateMarginal || other.truncateMarginal)) } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt index 8a8f666c90..7434394541 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt @@ -58,4 +58,21 @@ interface Timeline, THIS: Timeline> { /** @suppress */ fun specialize() = ctor(this) + + /** + * Caches the result of collecting this timeline, to be reused for future collect requests if possible. + */ + fun cache(opts: CollectOptions) + + /** + * [(DOC)][cache] A simplified version of [cache]. + * + * Uses defaults for all other [CollectOptions] fields. + * + * @param bounds bounds of evaluation (defaults to [Interval.MIN_MAX] if not provided). + */ + fun cache(bounds: Interval) = cache(CollectOptions(bounds)) + + /** [(DOC)][collect] Caches the timeline for all available time. */ + fun cache() = cache(Interval.MIN_MAX) } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Numbers.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Numbers.kt index 8342b2353c..3de00c0085 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Numbers.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Numbers.kt @@ -129,17 +129,18 @@ data class Numbers(private val timeline: Timeline, Numbers /** Raises this to the power of a linear profile. */ infix fun pow(other: Real) = this pow other.toNumbers("Cannot apply a non-piecewise-constant exponent.") + private fun lessThanInternal(l: L, r: R) = + if (l is Double || r is Double) l.toDouble() < r.toDouble() + else if (l is Float || r is Float) l.toFloat() < r.toFloat() + else if (l is Long || r is Long) l.toLong() < r.toLong() + else if (l is Int || r is Int) l.toInt() < r.toInt() + else if (l is Short || r is Short) l.toShort() < r.toShort() + else if (l is Byte || r is Byte) l.toByte() < r.toByte() + else throw PrimitiveNumberOps.UnreachablePrimitiveNumberException() + /** Returns a [Booleans] that is true when this is less than another primitive numeric profile. */ infix fun lessThan(other: Numbers<*>) = - map2Values(::Booleans, other) { l, r, _ -> - if (l is Double || r is Double) l.toDouble() < r.toDouble() - else if (l is Float || r is Float) l.toFloat() < r.toFloat() - else if (l is Long || r is Long) l.toLong() < r.toLong() - else if (l is Int || r is Int) l.toInt() < r.toInt() - else if (l is Short || r is Short) l.toShort() < r.toShort() - else if (l is Byte || r is Byte) l.toByte() < r.toByte() - else throw PrimitiveNumberOps.UnreachablePrimitiveNumberException() - } + map2Values(::Booleans, other) { l, r, _ -> lessThanInternal(l, r) } /** Returns a [Booleans] that is true when this is less than a constant number. */ infix fun lessThan(n: Number) = lessThan(Numbers(n)) @@ -163,17 +164,18 @@ data class Numbers(private val timeline: Timeline, Numbers /** Returns a [Booleans] that is true when this is less than or equal to a linear profile. */ infix fun lessThanOrEqualTo(other: Real) = other greaterThanOrEqualTo this + private fun greaterThanInternal(l: L, r: R) = + if (l is Double || r is Double) l.toDouble() > r.toDouble() + else if (l is Float || r is Float) l.toFloat() > r.toFloat() + else if (l is Long || r is Long) l.toLong() > r.toLong() + else if (l is Int || r is Int) l.toInt() > r.toInt() + else if (l is Short || r is Short) l.toShort() > r.toShort() + else if (l is Byte || r is Byte) l.toByte() > r.toByte() + else throw PrimitiveNumberOps.UnreachablePrimitiveNumberException() + /** Returns a [Booleans] that is true when this is greater than another primitive numeric profile. */ infix fun greaterThan(other: Numbers<*>) = - map2Values(::Booleans, other) { l, r, _ -> - if (l is Double || r is Double) l.toDouble() > r.toDouble() - else if (l is Float || r is Float) l.toFloat() > r.toFloat() - else if (l is Long || r is Long) l.toLong() > r.toLong() - else if (l is Int || r is Int) l.toInt() > r.toInt() - else if (l is Short || r is Short) l.toShort() > r.toShort() - else if (l is Byte || r is Byte) l.toByte() > r.toByte() - else throw PrimitiveNumberOps.UnreachablePrimitiveNumberException() - } + map2Values(::Booleans, other) { l, r, _ -> greaterThanInternal(l, r) } /** Returns a [Booleans] that is true when this is greater than a constant number. */ infix fun greaterThan(n: Number) = greaterThan(Numbers(n)) @@ -202,6 +204,9 @@ data class Numbers(private val timeline: Timeline, Numbers @Suppress("UNCHECKED_CAST") override fun shiftedDifference(range: Duration) = (shift(-range) - this) as Numbers + override fun increases() = detectEdges(NullBinaryOperation.combineOrNull { l, r, _ -> lessThanInternal(l, r) }) + override fun decreases() = detectEdges(NullBinaryOperation.combineOrNull { l, r, _ -> greaterThanInternal(l, r) }) + /***/ companion object { /** * Converts a list of serialized value segments into a [Numbers] profile; diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt index be61bade60..dbe770e2ff 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt @@ -20,9 +20,7 @@ data class Real(private val timeline: Timeline, Real>): SerialNumericOps, LinearOps { - constructor(v: Int): this(v.toDouble()) - constructor(v: Long): this(v.toDouble()) - constructor(v: Double): this(LinearEquation(v)) + constructor(v: Number): this(LinearEquation(v.toDouble())) constructor(eq: LinearEquation): this(Segment(Interval.MIN_MAX, eq)) constructor(vararg segments: Segment): this(segments.asList()) constructor(segments: List>): this(BaseTimeline(::Real, preprocessList(segments, Segment::valueEquals))) @@ -138,32 +136,32 @@ data class Real(private val timeline: Timeline, Real>): private fun inequalityHelper(other: SerialNumericOps<*, *>, f: LinearEquation.(LinearEquation) -> Booleans) = flatMap2Values(::Booleans, other.toReal()) { l, r, _ -> l.f(r) } - - override fun changes() = - unsafeOperate(::Booleans) { opts -> - val bounds = opts.bounds - var previous: Segment? = null - val result = collect(CollectOptions(bounds, false)).flatMap { currentSegment: Segment -> - val currentInterval = currentSegment.interval - val leftEdge = if ( - previous !== null && - previous!!.interval.compareEndToStart(currentInterval) == 0 && - currentInterval.includesStart() - ) { - previous!!.value.valueAt(currentInterval.start) == currentSegment.value.valueAt(currentInterval.start) - } else if (currentInterval.compareStarts(bounds) == 0) { - currentSegment.value.rate != 0.0 - } else { - null - } - previous = currentSegment - listOfNotNull( - Segment(Interval.at(currentInterval.start), leftEdge).transpose(), - Segment(Interval.between(currentInterval.start, currentInterval.end, Interval.Inclusivity.Exclusive), currentSegment.value.rate != 0.0) - ) - } - truncateList(result, opts) + private fun detectChangesInternal(leftEdgeFilter: (Double, Double) -> Boolean, continuousFilter: (Double) -> Boolean) = unsafeOperate(::Booleans) { opts -> + val bounds = opts.bounds + var previous: Segment? = null + val result = collect(CollectOptions(bounds, false)).flatMap { currentSegment: Segment -> + val currentInterval = currentSegment.interval + val leftEdge = if ( + previous !== null && + previous!!.interval.compareEndToStart(currentInterval) == 0 && + currentInterval.includesStart() + ) { + leftEdgeFilter(previous!!.value.valueAt(currentInterval.start), currentSegment.value.valueAt(currentInterval.start)) + } else if (currentInterval.compareStarts(bounds) == 0) { + continuousFilter(currentSegment.value.rate) + } else { + null } + previous = currentSegment + listOfNotNull( + Segment(Interval.at(currentInterval.start), leftEdge).transpose(), + Segment(Interval.between(currentInterval.start, currentInterval.end, Interval.Inclusivity.Exclusive), continuousFilter(currentSegment.value.rate)) + ) + } + truncateList(result, opts, true, true) + } + + override fun changes() = detectChangesInternal({ l, r -> l != r }, { it != 0.0 }) /** * Returns a [Booleans] that is true whenever this discontinuously transitions between @@ -177,6 +175,19 @@ data class Real(private val timeline: Timeline, Real>): override fun shiftedDifference(range: Duration) = shift(-range).minus(this) + override fun increases() = detectChangesInternal({ l, r -> l < r }, { it > 0.0}) + override fun decreases() = detectChangesInternal({ l, r -> l > r }, { it < 0.0}) + + private class UnreachableValueAtException: Exception("internal error. a serial profile had multiple values at the same time.") + + /** Calculates the value of the profile at the given time. */ + fun sample(time: Duration): Double? { + val list = collect(CollectOptions(Interval.at(time), true)) + if (list.isEmpty()) return null + if (list.size > 1) throw UnreachableValueAtException() + return list[0].value.valueAt(time) + } + /** * An exception for linear profile operations; usually thrown in contexts that * require one or more of the operands to be piecewise constant. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Strings.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Strings.kt new file mode 100644 index 0000000000..78ce7840c6 --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Strings.kt @@ -0,0 +1,60 @@ +package gov.nasa.ammos.aerie.procedural.timeline.collections.profiles + +import gov.nasa.ammos.aerie.procedural.timeline.* +import gov.nasa.ammos.aerie.procedural.timeline.ops.SerialConstantOps +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue + +/** + * A profile of Strings. + * + * @see String + */ +data class Strings(private val timeline: Timeline, Strings>): + Timeline, Strings> by timeline, + SerialConstantOps +{ + constructor(v: String): this(Segment(Interval.MIN_MAX, v)) + constructor(vararg segments: Segment): this(segments.asList()) + constructor(segments: List>): this( + BaseTimeline( + ::Strings, + preprocessList(segments, Segment::valueEquals) + ) + ) + + /** [(DOC)][length] Returns a [Numbers] profile of the lengths. */ + fun length() = mapValues(::Numbers) { it.value.length } + + /** Check for string equality, case-insensitive. */ + fun caseInsensitiveEqualTo(other: SerialConstantOps) = + map2Values(::Booleans, other) { l, r, _ -> l.equals(r, ignoreCase = true) } + + /** Check for string equality with a single string, case-insensitive. */ + fun caseInsensitiveEqualTo(other: String) = caseInsensitiveEqualTo(Strings(other)) + + /** Check for string inequality, case-insensitive. */ + fun caseInsensitiveNotEqualTo(other: SerialConstantOps) = + map2Values(::Booleans, other) { l, r, _ -> !l.equals(r, ignoreCase = true) } + + /** Check for string inequality with a single string, case-insensitive. */ + fun caseInsensitiveNotEqualTo(other: String) = caseInsensitiveNotEqualTo(Strings(other)) + + /** Returns a [Booleans] that is true when this is the empty string. */ + fun isEmpty() = mapValues(::Booleans) { it.value.isEmpty() } + + /** Returns a [Booleans] that is true when this is not the empty string. */ + fun isNotEmpty() = mapValues(::Booleans) { it.value.isNotEmpty() } + + /***/ companion object { + /** + * Converts a list of serialized value segments into a [Strings] profile; + * for use with [gov.nasa.ammos.aerie.procedural.timeline.plan.Plan.resource]. + */ + @JvmStatic fun deserialize(list: List>) = Strings(list.map { seg -> + val string = seg.value.asString().orElseThrow { Exception("value was not a string: $seg") } + seg.withNewValue(string) + }) + } +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt index 9295b7675e..a09b6a657a 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt @@ -167,7 +167,7 @@ interface GeneralOps, THIS: GeneralOps>: Timeline, RESULT: GeneralOps> unsafeMap(ctor: (Timeline) -> RESULT, boundsTransformer: BoundsTransformer, truncate: Boolean, f: (V) -> R) = unsafeOperate(ctor) { opts -> val mapped = collect(opts.transformBounds(boundsTransformer)).map { f(it) } - if (truncate) truncateList(mapped, opts) + if (truncate) truncateList(mapped, opts, false, false) else mapped } @@ -195,7 +195,7 @@ interface GeneralOps, THIS: GeneralOps>: Timeline, THIS: GeneralOps>: Timeline Boolean) = unsafeOperate { opts -> val result = collect(CollectOptions(opts.bounds, !preserveMargin && opts.truncateMarginal)).filter(f) - if (preserveMargin && opts.truncateMarginal) truncateList(result, opts) + if (preserveMargin && opts.truncateMarginal) truncateList(result, opts, false, false) else result } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/NonZeroDurationOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/NonZeroDurationOps.kt index 7661cbc870..ab0c68f447 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/NonZeroDurationOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/NonZeroDurationOps.kt @@ -53,7 +53,7 @@ interface NonZeroDurationOps, THIS: NonZeroDurationOps, THIS: ParallelOps>: GeneralOp remaining = partitioned.second acc = map2SegmentLists(batch, acc, op) } - if (!opts.truncateMarginal) truncateList(acc, opts) + if (opts.truncateMarginal) truncateList(acc, opts, true, true) else acc } @@ -163,7 +163,7 @@ interface ParallelOps, THIS: ParallelOps>: GeneralOp fun starts() = unsafeOperate { opts -> val result = collect(CollectOptions(opts.bounds, false)) .map { it.withNewInterval(Interval.at(it.interval.start)) } - truncateList(result, opts) + truncateList(result, opts, false, false) } /** @@ -175,7 +175,7 @@ interface ParallelOps, THIS: ParallelOps>: GeneralOp fun ends() = unsafeOperate { opts -> val result = collect(CollectOptions(opts.bounds, false)) .map { it.withNewInterval(Interval.at(it.interval.end)) } - truncateList(result, opts) + truncateList(result, opts, false, false) } /** diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialConstantOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialConstantOps.kt index 207f2c4d1f..de90106d69 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialConstantOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialConstantOps.kt @@ -1,5 +1,6 @@ package gov.nasa.ammos.aerie.procedural.timeline.ops +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.ammos.aerie.procedural.timeline.* import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Constants @@ -35,4 +36,14 @@ interface SerialConstantOps>: SerialSeg { r, _ -> if (r == to) null else false }, { l, r, _ -> l == from && r == to } )) + + private class UnreachableValueAtException: Exception("internal error. a serial profile had multiple values at the same time.") + + /** [(DOC)][sample] Calculates the value of the profile at the given time. */ + fun sample(time: Duration): V? { + val list = collect(CollectOptions(Interval.at(time), true)) + if (list.isEmpty()) return null + if (list.size > 1) throw UnreachableValueAtException() + return list[0].value + } } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOps.kt index 9ad331c325..98b4235d6d 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOps.kt @@ -146,7 +146,7 @@ interface SerialSegmentOps>: SerialOps< Segment(rightEdgeInterval, rightEdge).transpose() ) } - truncateList(coalesceList(result, Segment::valueEquals), opts) + truncateList(coalesceList(result, Segment::valueEquals), opts, true, true) } /** diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/SerialNumericOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/SerialNumericOps.kt index 3e94095b9f..6f62fcabd0 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/SerialNumericOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/numeric/SerialNumericOps.kt @@ -1,6 +1,7 @@ package gov.nasa.ammos.aerie.procedural.timeline.ops.numeric import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real @@ -56,4 +57,18 @@ interface SerialNumericOps>: SerialSegme * If this is a function `f(t)`, the result is `f(t+range) - f(t)`. */ fun shiftedDifference(range: Duration): THIS + + /** + * [(DOC)][increases] Returns a [Booleans] that is true whenever this profile increases, and false or gap everywhere else. + * + * This includes both continuous and discontinuous increases. + */ + fun increases(): Booleans + + /** + * [(DOC)][decreases] Returns a [Booleans] that is true whenever this profile decreases, and false or gap everywhere else. + * + * This includes both continuous and discontinuous increases. + */ + fun decreases(): Booleans } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/LinearEquation.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/LinearEquation.kt index 7a63558a08..3e3be12df8 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/LinearEquation.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/LinearEquation.kt @@ -21,7 +21,7 @@ data class LinearEquation( @JvmField val rate: Double ) { /** Creates a constant linear equation at a given value. */ - constructor(constant: Double): this(Duration.ZERO, constant, 0.0) + constructor(constant: Number): this(Duration.ZERO, constant.toDouble(), 0.0) /** Calculates the value at a given time. */ fun valueAt(time: Duration): Double { diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListUtils.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListUtils.kt index 4adf2bb73e..1bcb897faf 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListUtils.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListUtils.kt @@ -1,5 +1,6 @@ package gov.nasa.ammos.aerie.procedural.timeline.util +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions import gov.nasa.ammos.aerie.procedural.timeline.Interval import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike @@ -20,29 +21,184 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike fun > preprocessList(list: List, shouldCoalesce: (V.(V) -> Boolean)?): (CollectOptions) -> List { val sorted = list.sorted() val coalesced = maybeCoalesce(sorted, shouldCoalesce) - return listCollector(coalesced) + return listCollector(coalesced, true, shouldCoalesce != null) } /** Returns a function that lazily truncates a given list of timeline objects to the bounds. */ -fun > listCollector(list: List) = { opts: CollectOptions -> truncateList(list, opts) } +fun > listCollector(list: List, isSorted: Boolean = false, isSerial: Boolean = false) = + { opts: CollectOptions -> truncateList(list, opts, isSorted, isSerial) } /** Eagerly truncates a list of timeline objects to known bounds. */ -fun > truncateList(list: List, opts: CollectOptions) = - if (opts.bounds == Interval.MIN_MAX) list - else if (!opts.truncateMarginal) { - list.filter { - val intersection = it.interval.intersection(opts.bounds) - !intersection.isEmpty() +fun > truncateList(list: List, opts: CollectOptions, exploitSorted: Boolean, isSerial: Boolean): List = + if (opts.bounds == Interval.MIN_MAX || list.isEmpty()) list + else if (!exploitSorted) { + if (!opts.truncateMarginal) { + list.filter { + val intersection = it.interval intersection opts.bounds + !intersection.isEmpty() + } + } else { + list.mapNotNull { + val intersection = it.interval intersection opts.bounds + if (intersection.isEmpty()) null + else if (intersection == it.interval) it + else it.withNewInterval(intersection) + } } } else { - list.mapNotNull { - val intersection = it.interval.intersection(opts.bounds) - if (intersection.isEmpty()) null - else if (intersection == it.interval) it - else it.withNewInterval(intersection) + var endIndex = binarySearch(list, opts.bounds.end, true, opts.bounds.endInclusivity == Interval.Inclusivity.Inclusive) + ?: list.size + + if (isSerial) { + val startIndex = ( + binarySearch(list, opts.bounds.start, false, opts.bounds.startInclusivity == Interval.Inclusivity.Inclusive) + ?: -1 + ) + 1 + var result = list.subList(startIndex, endIndex) + if (result.isEmpty()) result + else if (!opts.truncateMarginal) { + result + } else { + result = result.toMutableList() + result[0] = result[0].withNewInterval(result[0].interval intersection opts.bounds) + + endIndex = result.size - 1 + result[endIndex] = result[endIndex].withNewInterval(result[endIndex].interval intersection opts.bounds) + + result + } + } else { + if (!opts.truncateMarginal) { + val guaranteedStartIndex = binarySearch(list, opts.bounds.start, true, false) ?: list.size + val preList = truncateList(list.subList(0, guaranteedStartIndex), opts, false, false) + val result = list.subList(guaranteedStartIndex, endIndex) + preList + result + } else { + truncateList(list.subList(0, endIndex), opts, false, false) + } } } +/** + * If [searchByStartTime] is true, returns the index of the FIRST interval that STARTS AFTER OR EQUAL to the target. + * + * Alternatively if [searchByStartTime] is false, returns the index of the LAST interval + * that ENDS BEFORE OR EQUAL to the search target. + * + * If [strict] is true, restricts the above to strictly before and strictly after, respectively. + * + * If no such interval exists, returns null. + */ +private fun > binarySearch(list: List, target: Duration, searchByStartTime: Boolean, strict: Boolean): Int? { + // Define a function to compare an interval to the target. + // [searchByStartTime] determines whether we compare with the start or end of the interval. + val comparator: (Interval) -> Int = + if (searchByStartTime) fun(i: Interval): Int { + val startComparison = i.start.compareTo(target) + return if (startComparison == 0) { + when (i.startInclusivity) { + Interval.Inclusivity.Inclusive -> 0 + Interval.Inclusivity.Exclusive -> 1 + } + } else startComparison + } + else fun(i: Interval): Int { + val endComparison = i.end.compareTo(target) + return if (endComparison == 0) { + when (i.endInclusivity) { + Interval.Inclusivity.Inclusive -> 0 + Interval.Inclusivity.Exclusive -> -1 + } + } else endComparison + } + + // Define a function to calculate the middle index of the search window. + // searchByStartTime determines whether we round up or down. + val middleCalculator = if (searchByStartTime) { + fun(start: Int, end: Int) = (start + end) / 2 + } else { + fun(start: Int, end: Int): Int { + val sum = start + end + return (sum / 2) + (sum % 2) + } + } + + var indexStart = 0 + var indexEnd = list.size - 1 + + // Define a function to narrow down the search window depending on the comparison result. + + // If searchByStartTime, we add 1 when selecting the right half. + // If !searchByStartTime, we subtract 1 when selecting the left half. + + // If strict == searchByStartTime, we select the right half on 0's. + // If strict ^ searchByStartTime, we select the left half on 0's. + val comparisonAction = + if (searchByStartTime) { + if (!strict) { + fun(c: Int, middle: Int) { + when (c) { + -1 -> indexStart = middle + 1 + 0, 1 -> indexEnd = middle + else -> throw IndexOutOfBoundsException() + } + } + } else { + fun(c: Int, middle: Int) { + when (c) { + -1, 0 -> indexStart = middle + 1 + 1 -> indexEnd = middle + else -> throw IndexOutOfBoundsException() + } + } + } + } else { + if (!strict) { + fun(c: Int, middle: Int) { + when (c) { + -1, 0 -> indexStart = middle + 1 -> indexEnd = middle - 1 + else -> throw IndexOutOfBoundsException() + } + } + } else { + fun(c: Int, middle: Int) { + when (c) { + -1 -> indexStart = middle + 0, 1 -> indexEnd = middle - 1 + else -> throw IndexOutOfBoundsException() + } + } + } + } + + // Perform the search. + while (indexStart != indexEnd) { + val middle = middleCalculator(indexStart, indexEnd) + val comparison = comparator(list[middle].interval) + comparisonAction(comparison, middle) + } + + // Check if we didn't find any value results. + + // We return null according to these rules: + + // if searchByStartTime, return null if the comparison is "too low" + // if !searchByStartTime, return null if the comparison is "too high" + + // if strict, "too low" means -1 or 0, and "too high" means 0 or 1 + // if !strict, "too low" means only -1, and "too high" means only 1 + return if (searchByStartTime) { + if (!strict && comparator(list[indexStart].interval) < 0) null + else if (strict && comparator(list[indexStart].interval) < 1) null + else indexStart + } else { + if (!strict && comparator(list[indexStart].interval) > 0) null + else if (strict && comparator(list[indexStart].interval) > -1) null + else indexStart + } +} + /** Returns a new list of intervals, sorted by start time, or end time in case of a tie. */ fun > List.sorted() = sortedWith { a, b -> val startComparison = a.interval.compareStarts(b.interval) diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimelineTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimelineTest.kt new file mode 100644 index 0000000000..5420499280 --- /dev/null +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimelineTest.kt @@ -0,0 +1,50 @@ +package gov.nasa.ammos.aerie.procedural.timeline + +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Constants +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo + +class BaseTimelineTest { + @Test + fun cache() { + var collectCounter = 0 + + val profile = BaseTimeline(::Constants) { + collectCounter += 1 + listOf>() + } + + assertEquals(collectCounter, 0) + + // collect once + profile.collect() + assertEquals(collectCounter, 1) + + // cache on small bounds, triggering a eval + profile.cache(seconds(0) .. seconds(5)) + assertEquals(collectCounter, 2) + + // collect on cached bounds, no-op + profile.collect(Interval.Companion.at(seconds(3))) + assertEquals(collectCounter, 2) + + // collect on wider bounds, triggering an eval + profile.collect() + assertEquals(collectCounter, 3) + + // cache on wider bounds, triggering an eval + profile.cache() + assertEquals(collectCounter, 4) + + // collect on cached bounds, no-op + profile.collect() + assertEquals(collectCounter, 4) + + // cache on smaller bounds, no-op + profile.cache(seconds(2)..seconds(10)) + assertEquals(collectCounter, 4) + } +} diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BoundsTransformerTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BoundsTransformerTest.kt index e6da888551..cd9378c5c1 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BoundsTransformerTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BoundsTransformerTest.kt @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test class BoundsTransformerTest { @Test fun shift() { - val transformer = BoundsTransformer.shift(seconds(1)) + val transformer = BoundsTransformer.Companion.shift(seconds(1)) assertEquals( between(seconds(-1), seconds(1)), diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/WindowsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/WindowsTest.kt index c26b56de17..b0b697ee60 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/WindowsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/WindowsTest.kt @@ -16,10 +16,10 @@ class WindowsTest { @Test fun constructorCoalesce() { val result = Windows( - seconds(2) .. seconds(3), - seconds(0) ..< seconds(2), + seconds(2)..seconds(3), + seconds(0).. Date: Tue, 6 Aug 2024 17:12:08 -0700 Subject: [PATCH 017/108] Use ActivityDirectiveId and ActivityInstanceId in timeline --- procedural/constraints/build.gradle | 1 + .../aerie/procedural/constraints/Violations.kt | 4 ++-- .../aerie/procedural/remote/AeriePostgresPlan.kt | 9 +++++---- .../remote/AeriePostgresSimulationResults.kt | 6 ++++-- .../procedural/scheduling/plan/NewDirective.kt | 3 ++- .../timeline/payloads/activities/Directive.kt | 13 +++++++------ .../payloads/activities/DirectiveStart.kt | 11 ++++++----- .../timeline/payloads/activities/Instance.kt | 16 +++++++++------- 8 files changed, 36 insertions(+), 27 deletions(-) diff --git a/procedural/constraints/build.gradle b/procedural/constraints/build.gradle index 7d637e5983..f425e980fc 100644 --- a/procedural/constraints/build.gradle +++ b/procedural/constraints/build.gradle @@ -14,6 +14,7 @@ repositories { dependencies { implementation project(':procedural:timeline') + implementation project(':merlin-driver') testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt index bd93ae5de6..73fe96dd2d 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt @@ -72,8 +72,8 @@ data class Violations(private val timeline: Timeline): )} private fun > V.getActivityId() = when (this) { - is Instance<*> -> InstanceId(id) - is Directive<*> -> DirectiveId(id) + is Instance<*> -> InstanceId(id.id) + is Directive<*> -> DirectiveId(id.id) else -> null } } diff --git a/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresPlan.kt b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresPlan.kt index b949a8c15d..f520f28052 100644 --- a/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresPlan.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresPlan.kt @@ -12,6 +12,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveSta import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan import gov.nasa.ammos.aerie.procedural.timeline.util.duration.plus import gov.nasa.ammos.aerie.procedural.timeline.util.duration.minus +import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId import java.io.StringReader import java.sql.Connection import java.time.Instant @@ -85,12 +86,12 @@ data class AeriePostgresPlan( val offset = Duration.parseISO8601(response.getString(2)) val start = if (anchorId != 0L) { // this means SQL null. Terrible interface imo val anchoredToStart = response.getBoolean(7) - DirectiveStart.Anchor(anchorId, offset, if (anchoredToStart) DirectiveStart.Anchor.AnchorPoint.Start else DirectiveStart.Anchor.AnchorPoint.End, Duration.ZERO) + DirectiveStart.Anchor(ActivityDirectiveId(anchorId), offset, if (anchoredToStart) DirectiveStart.Anchor.AnchorPoint.Start else DirectiveStart.Anchor.AnchorPoint.End, Duration.ZERO) } else DirectiveStart.Absolute(offset) unresolved.add(Directive( parseJson(response.getString(4)), response.getString(1), - response.getLong(5), + ActivityDirectiveId(response.getLong(5)), response.getString(3), start )) @@ -104,7 +105,7 @@ data class AeriePostgresPlan( result.add(it) } is DirectiveStart.Anchor -> { - val index = result.binarySearch { a -> a.id.compareTo(s.parentId) } + val index = result.binarySearch { a -> a.id.id.compareTo(s.parentId.id) } if (index >= 0) { val parent = result[index] s.estimatedStart = parent.startTime + s.offset @@ -114,7 +115,7 @@ data class AeriePostgresPlan( } }.toMutableList() if (sizeAtStartOfStep == unresolved.size) throw Error("Cannot resolve anchors: $unresolved") - result.sortBy { it.id } + result.sortBy { it.id.id } } result }.specialize() diff --git a/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt index 95eda666f5..cb65cac928 100644 --- a/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt @@ -12,6 +12,8 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Instance import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults +import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId +import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId import java.io.StringReader import java.sql.Connection import javax.json.Json @@ -164,8 +166,8 @@ data class AeriePostgresSimulationResults( result.add(Instance( deserializer(attributes), response.getString(4), - id, - directiveId, + ActivityInstanceId(id), + directiveId?.let { ActivityDirectiveId(it) }, between(start, start.plus(Duration.parseISO8601(response.getString(2)))) )) } diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt index 868e38ce37..009147de56 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt @@ -3,6 +3,7 @@ package gov.nasa.ammos.aerie.procedural.scheduling.plan import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart +import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId /** A new directive to be created, which doesn't have an id yet. */ data class NewDirective( @@ -27,7 +28,7 @@ data class NewDirective( * @param id The id for the new directive. * @param parent The activity this activity is anchored to, if applicable. */ - fun resolve(id: Long, parent: Directive<*>?): Directive { + fun resolve(id: ActivityDirectiveId, parent: Directive<*>?): Directive { if (start is DirectiveStart.Anchor) { if (parent == null) throw IllegalArgumentException("Parent must provided when anchor is not null") start.estimatedStart = parent.startTime + start.offset diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt index f1af0ea611..a1ccc1395c 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt @@ -2,21 +2,22 @@ package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId /** A wrapper of any type of activity directive containing common data. */ data class Directive( - /** The inner payload, typically either [AnyDirective] or a mission model activity type. */ + /** The inner payload, typically either [AnyDirective] or a mission model activity type. */ @JvmField val inner: A, - /** The name of this specific directive. */ + /** The name of this specific directive. */ @JvmField val name: String, - /** The directive id. */ - @JvmField val id: Long, + /** The directive id. */ + @JvmField val id: ActivityDirectiveId, - override val type: String, + override val type: String, - /** The start behavior for this directive. */ + /** The start behavior for this directive. */ val start: DirectiveStart, ): Activity> { override val startTime: Duration diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/DirectiveStart.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/DirectiveStart.kt index 375f16fc88..973f0aff3b 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/DirectiveStart.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/DirectiveStart.kt @@ -1,5 +1,6 @@ package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities +import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId import gov.nasa.jpl.aerie.merlin.protocol.types.Duration /** Start behavior for an activity directive. */ @@ -14,16 +15,16 @@ sealed interface DirectiveStart { /** For activities that are anchored to another activity. */ data class Anchor @JvmOverloads constructor( - /** Id of the parent it is anchored to. */ - val parentId: Long, + /** Id of the parent it is anchored to. */ + val parentId: ActivityDirectiveId, - /** Duration to offset from the parent (negative durations mean before the parent). */ + /** Duration to offset from the parent (negative durations mean before the parent). */ val offset: Duration, - /** Which end of the parent to anchor to. */ + /** Which end of the parent to anchor to. */ val anchorPoint: AnchorPoint, - /** + /** * When the activity is estimated to start (approximate, and automatically set by [EditablePlan] implementations). * * Defaults to [Duration.ZERO] (plan start). diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt index 183275412e..432bd4e732 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt @@ -2,23 +2,25 @@ package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId +import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId /** A wrapper of any type of activity instance containing common data. */ data class Instance( - /** The inner payload, typically either [AnyInstance] or a mission model activity type. */ + /** The inner payload, typically either [AnyInstance] or a mission model activity type. */ @JvmField val inner: A, - override val type: String, + override val type: String, - /** The instance id. */ - @JvmField val id: Long, + /** The instance id. */ + @JvmField val id: ActivityInstanceId, - /** + /** * The maybe-null id of the directive associated with this instance. * * Will be `null` if this is a child activity. */ - @JvmField val directiveId: Long?, - override val interval: Interval, + @JvmField val directiveId: ActivityDirectiveId?, + override val interval: Interval, ): Activity> { override val startTime: Duration get() = interval.start From 5acf7d5f1b2ccd90245a49b26de5c055798fb32e Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Tue, 9 Jul 2024 10:30:42 -0700 Subject: [PATCH 018/108] Return initial sim results if latest is null --- .../scheduler/simulation/CheckpointSimulationFacade.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java index 9a921a510b..7b22b189ee 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java @@ -329,6 +329,9 @@ public SimulationData simulateWithResults( @Override public Optional getLatestSimulationData() { - return Optional.ofNullable(this.latestSimulationData); + if (this.latestSimulationData == null) + return Optional.ofNullable(this.initialSimulationResults); + else + return Optional.ofNullable(this.latestSimulationData); } } From ac8c53c51b3b8137c5d9944972e6870fb4e3cc80 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Tue, 6 Aug 2024 20:36:52 -0700 Subject: [PATCH 019/108] Use switch instead of if in GoalBuilder --- .../server/remotes/postgres/GoalBuilder.java | 182 +++++++++--------- 1 file changed, 94 insertions(+), 88 deletions(-) diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GoalBuilder.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GoalBuilder.java index 6059c06b7a..008208cfdc 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GoalBuilder.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GoalBuilder.java @@ -47,8 +47,9 @@ public static Goal goalOfGoalSpecifier( horizonStartTimestamp.toInstant(), horizonEndTimestamp.toInstant()); final var hor = planningHorizon.getHor(); - if (goalSpecifier instanceof SchedulingDSL.GoalSpecifier.RecurrenceGoalDefinition g) { - final var builder = new RecurrenceGoal.Builder() + switch (goalSpecifier) { + case SchedulingDSL.GoalSpecifier.RecurrenceGoalDefinition g -> { + final var builder = new RecurrenceGoal.Builder() .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor, true))) .separatedByAtLeast(g.separatedByAtLeast()) .separatedByAtMost(g.separatedByAtMost()) @@ -61,101 +62,106 @@ public static Goal goalOfGoalSpecifier( } if(g.activityFinder().isPresent()){ builder.match(buildActivityExpression(g.activityFinder().get(), lookupActivityType)); - } - return builder.build(); - } else if (goalSpecifier instanceof SchedulingDSL.GoalSpecifier.CoexistenceGoalDefinition g) { - var builder = new CoexistenceGoal.Builder() - .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor, true))) - .createPersistentAnchor(g.persistentAnchor().isPresent()? g.persistentAnchor().get() : PersistentTimeAnchor.DISABLED) - .forEach(spansOfConstraintExpression( - g.forEach())) - .thereExistsOne(makeActivityTemplate(g.activityTemplate(), lookupActivityType)) - .withinPlanHorizon(planningHorizon) - .simulateAfter(simulateAfter) - .shouldRollbackIfUnsatisfied(g.shouldRollbackIfUnsatisfied()) - .aliasForAnchors(g.alias()); - if (g.startConstraint().isPresent()) { - final var startConstraint = g.startConstraint().get(); - if (startConstraint instanceof SchedulingDSL.TimingConstraint.ActivityTimingConstraint s) { - builder.startsAt(makeTimeExpressionRelativeSimple(s)); - } else if (startConstraint instanceof SchedulingDSL.TimingConstraint.ActivityTimingConstraintFlexibleRange s) { - builder.startsAt(new TimeExpressionRelativeBinary(makeTimeExpressionRelativeSimple(s.lowerBound()), makeTimeExpressionRelativeSimple(s.upperBound()))); - } else { - throw new UnexpectedSubtypeError(SchedulingDSL.TimingConstraint.class, startConstraint); } + return builder.build(); } - if (g.endConstraint().isPresent()) { - final var endConstraint = g.endConstraint().get(); - if (endConstraint instanceof SchedulingDSL.TimingConstraint.ActivityTimingConstraint e) { - builder.endsAt(makeTimeExpressionRelativeSimple(e)); - } else if (endConstraint instanceof SchedulingDSL.TimingConstraint.ActivityTimingConstraintFlexibleRange e) { - builder.endsAt(new TimeExpressionRelativeBinary(makeTimeExpressionRelativeSimple(e.lowerBound()), makeTimeExpressionRelativeSimple(e.upperBound()))); - } else { - throw new UnexpectedSubtypeError(SchedulingDSL.TimingConstraint.class, endConstraint); + + case SchedulingDSL.GoalSpecifier.CoexistenceGoalDefinition g -> { + var builder = new CoexistenceGoal.Builder() + .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor, true))) + .createPersistentAnchor(g.persistentAnchor().isPresent()? g.persistentAnchor().get() : PersistentTimeAnchor.DISABLED) + .forEach(spansOfConstraintExpression( + g.forEach())) + .thereExistsOne(makeActivityTemplate(g.activityTemplate(), lookupActivityType)) + .withinPlanHorizon(planningHorizon) + .simulateAfter(simulateAfter) + .shouldRollbackIfUnsatisfied(g.shouldRollbackIfUnsatisfied()) + .aliasForAnchors(g.alias()); + if (g.startConstraint().isPresent()) { + final var startConstraint = g.startConstraint().get(); + if (startConstraint instanceof SchedulingDSL.TimingConstraint.ActivityTimingConstraint s) { + builder.startsAt(makeTimeExpressionRelativeSimple(s)); + } else if (startConstraint instanceof SchedulingDSL.TimingConstraint.ActivityTimingConstraintFlexibleRange s) { + builder.startsAt(new TimeExpressionRelativeBinary(makeTimeExpressionRelativeSimple(s.lowerBound()), makeTimeExpressionRelativeSimple(s.upperBound()))); + } else { + throw new UnexpectedSubtypeError(SchedulingDSL.TimingConstraint.class, startConstraint); + } } + if (g.endConstraint().isPresent()) { + final var endConstraint = g.endConstraint().get(); + if (endConstraint instanceof SchedulingDSL.TimingConstraint.ActivityTimingConstraint e) { + builder.endsAt(makeTimeExpressionRelativeSimple(e)); + } else if (endConstraint instanceof SchedulingDSL.TimingConstraint.ActivityTimingConstraintFlexibleRange e) { + builder.endsAt(new TimeExpressionRelativeBinary(makeTimeExpressionRelativeSimple(e.lowerBound()), makeTimeExpressionRelativeSimple(e.upperBound()))); + } else { + throw new UnexpectedSubtypeError(SchedulingDSL.TimingConstraint.class, endConstraint); + } + } + if (g.startConstraint().isEmpty() && g.endConstraint().isEmpty()) { + throw new Error("Both start and end constraints were empty. This should have been disallowed at the type level."); + } + if(g.activityFinder().isPresent()){ + builder.match(buildActivityExpression(g.activityFinder().get(), lookupActivityType)); + } + return builder.build(); } - if (g.startConstraint().isEmpty() && g.endConstraint().isEmpty()) { - throw new Error("Both start and end constraints were empty. This should have been disallowed at the type level."); - } - if(g.activityFinder().isPresent()){ - builder.match(buildActivityExpression(g.activityFinder().get(), lookupActivityType)); - } - return builder.build(); - } else if (goalSpecifier instanceof SchedulingDSL.GoalSpecifier.GoalAnd g) { - var builder = new CompositeAndGoal.Builder(); - for (final var subGoalSpecifier : g.goals()) { - builder = builder.and(goalOfGoalSpecifier(subGoalSpecifier, - horizonStartTimestamp, - horizonEndTimestamp, - lookupActivityType, - simulateAfter)); - } - builder.simulateAfter(simulateAfter); - builder.withinPlanHorizon(planningHorizon); - builder.forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor, true))); - builder.shouldRollbackIfUnsatisfied(g.shouldRollbackIfUnsatisfied()); - return builder.build(); - } else if (goalSpecifier instanceof SchedulingDSL.GoalSpecifier.GoalOr g) { - var builder = new OptionGoal.Builder(); - for (final var subGoalSpecifier : g.goals()) { - builder = builder.or(goalOfGoalSpecifier(subGoalSpecifier, - horizonStartTimestamp, - horizonEndTimestamp, - lookupActivityType, - simulateAfter)); - } - builder.simulateAfter(simulateAfter); - builder.withinPlanHorizon(planningHorizon); - builder.forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor, true))); - builder.shouldRollbackIfUnsatisfied(g.shouldRollbackIfUnsatisfied()); - return builder.build(); - } - else if (goalSpecifier instanceof SchedulingDSL.GoalSpecifier.GoalApplyWhen g) { - var goal = goalOfGoalSpecifier(g.goal(), horizonStartTimestamp, horizonEndTimestamp, lookupActivityType, simulateAfter); - goal.setTemporalContext(g.windows()); - return goal; - } + case SchedulingDSL.GoalSpecifier.GoalAnd g -> { + var builder = new CompositeAndGoal.Builder(); + for (final var subGoalSpecifier : g.goals()) { + builder = builder.and(goalOfGoalSpecifier(subGoalSpecifier, + horizonStartTimestamp, + horizonEndTimestamp, + lookupActivityType, + simulateAfter)); + } + builder.simulateAfter(simulateAfter); + builder.withinPlanHorizon(planningHorizon); + builder.forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor, true))); + builder.shouldRollbackIfUnsatisfied(g.shouldRollbackIfUnsatisfied()); + return builder.build(); + } - else if(goalSpecifier instanceof SchedulingDSL.GoalSpecifier.CardinalityGoalDefinition g){ - final var builder = new CardinalityGoal.Builder() - .thereExistsOne(makeActivityTemplate(g.activityTemplate(), lookupActivityType)) - .simulateAfter(simulateAfter) - .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor, true))) - .withinPlanHorizon(planningHorizon) - .shouldRollbackIfUnsatisfied(g.shouldRollbackIfUnsatisfied()); - if(g.specification().duration().isPresent()){ - builder.duration(Interval.between(g.specification().duration().get(), Duration.MAX_VALUE)); + case SchedulingDSL.GoalSpecifier.GoalOr g -> { + var builder = new OptionGoal.Builder(); + for (final var subGoalSpecifier : g.goals()) { + builder = builder.or(goalOfGoalSpecifier(subGoalSpecifier, + horizonStartTimestamp, + horizonEndTimestamp, + lookupActivityType, + simulateAfter)); + } + builder.simulateAfter(simulateAfter); + builder.withinPlanHorizon(planningHorizon); + builder.forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor, true))); + builder.shouldRollbackIfUnsatisfied(g.shouldRollbackIfUnsatisfied()); + return builder.build(); } - if(g.specification().occurrence().isPresent()){ - builder.occurences(new Range<>(g.specification().occurrence().get(), Integer.MAX_VALUE)); + + case SchedulingDSL.GoalSpecifier.GoalApplyWhen g -> { + var goal = goalOfGoalSpecifier(g.goal(), horizonStartTimestamp, horizonEndTimestamp, lookupActivityType, simulateAfter); + goal.setTemporalContext(g.windows()); + return goal; } - if(g.activityFinder().isPresent()){ - builder.match(buildActivityExpression(g.activityFinder().get(), lookupActivityType)); + + case SchedulingDSL.GoalSpecifier.CardinalityGoalDefinition g -> { + final var builder = new CardinalityGoal.Builder() + .thereExistsOne(makeActivityTemplate(g.activityTemplate(), lookupActivityType)) + .simulateAfter(simulateAfter) + .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor, true))) + .withinPlanHorizon(planningHorizon) + .shouldRollbackIfUnsatisfied(g.shouldRollbackIfUnsatisfied()); + if (g.specification().duration().isPresent()) { + builder.duration(Interval.between(g.specification().duration().get(), Duration.MAX_VALUE)); + } + if (g.specification().occurrence().isPresent()) { + builder.occurences(new Range<>(g.specification().occurrence().get(), Integer.MAX_VALUE)); + } + if (g.activityFinder().isPresent()) { + builder.match(buildActivityExpression(g.activityFinder().get(), lookupActivityType)); + } + return builder.build(); } - return builder.build(); - } else { - throw new Error("Unhandled variant of GoalSpecifier:" + goalSpecifier); } } From 37259b496b6f9fb833fd66eac6f138a188c4561e Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Tue, 20 Aug 2024 11:37:00 -0700 Subject: [PATCH 020/108] Implement database changes for goal invocations --- .../scheduling_goal_analysis.yaml | 2 +- ...ling_goal_analysis_created_activities.yaml | 5 + ...g_goal_analysis_satisfying_activities.yaml | 5 + .../scheduling_specification_goals.yaml | 4 +- .../Aerie/9_goal_invocations/down.sql | 93 +++++++++++++++++++ .../Aerie/9_goal_invocations/up.sql | 90 ++++++++++++++++++ .../scheduling_goal_analysis.sql | 7 +- ...uling_goal_analysis_created_activities.sql | 19 ++-- ...ng_goal_analysis_satisfying_activities.sql | 19 ++-- .../scheduling_specification_goals.sql | 9 +- 10 files changed, 223 insertions(+), 30 deletions(-) create mode 100644 deployment/hasura/migrations/Aerie/9_goal_invocations/down.sql create mode 100644 deployment/hasura/migrations/Aerie/9_goal_invocations/up.sql diff --git a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml index a878d8638c..43c12ae278 100644 --- a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml +++ b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml @@ -25,7 +25,7 @@ array_relationships: using: manual_configuration: column_mapping: - goal_id: goal_id + goal_invocation_id: goal_invocation_id analysis_id: analysis_id remote_table: name: scheduling_goal_analysis_satisfying_activities diff --git a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis_created_activities.yaml b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis_created_activities.yaml index 6369561464..1a6c3605b2 100644 --- a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis_created_activities.yaml +++ b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis_created_activities.yaml @@ -7,6 +7,11 @@ object_relationships: - name: analysis using: foreign_key_constraint_on: analysis_id +- name: goal_analysis + using: + foreign_key_constraint_on: + - goal_invocation_id + - analysis_id select_permissions: - role: aerie_admin permission: diff --git a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis_satisfying_activities.yaml b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis_satisfying_activities.yaml index a96afb84bd..7c5640a1b8 100644 --- a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis_satisfying_activities.yaml +++ b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis_satisfying_activities.yaml @@ -7,6 +7,11 @@ object_relationships: - name: analysis using: foreign_key_constraint_on: analysis_id +- name: goal_analysis + using: + foreign_key_constraint_on: + - goal_invocation_id + - analysis_id select_permissions: - role: aerie_admin permission: diff --git a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_specification/scheduling_specification_goals.yaml b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_specification/scheduling_specification_goals.yaml index 20a1b09c00..b3d5254496 100644 --- a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_specification/scheduling_specification_goals.yaml +++ b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_specification/scheduling_specification_goals.yaml @@ -35,11 +35,11 @@ select_permissions: insert_permissions: - role: aerie_admin permission: - columns: [specification_id, goal_id, goal_revision, priority, enabled, simulate_after] + columns: [specification_id, goal_id, goal_revision, priority, enabled, simulate_after, goal_invocation_id] check: {} - role: user permission: - columns: [specification_id, goal_id, goal_revision, priority, enabled, simulate_after] + columns: [specification_id, goal_id, goal_revision, priority, enabled, simulate_after, goal_invocation_id] check: {} update_permissions: - role: aerie_admin diff --git a/deployment/hasura/migrations/Aerie/9_goal_invocations/down.sql b/deployment/hasura/migrations/Aerie/9_goal_invocations/down.sql new file mode 100644 index 0000000000..6aa4d6fef2 --- /dev/null +++ b/deployment/hasura/migrations/Aerie/9_goal_invocations/down.sql @@ -0,0 +1,93 @@ +-- restore goal_analysis_* tables + +-- drop new PKs for created / satisfying activities +-- and add old columns back so we can populate and re-PK +alter table scheduler.scheduling_goal_analysis_satisfying_activities + drop constraint satisfying_activities_primary_key, + drop constraint satisfying_activities_references_scheduling_goal_analysis, + + -- temp set as nullable so we can insert, made not null by PK constraint below + add column goal_id integer null, + add column goal_revision integer null; + +alter table scheduler.scheduling_goal_analysis_created_activities + drop constraint created_activities_primary_key, + drop constraint created_activities_references_scheduling_goal_analysis, + + add column goal_id integer null, + add column goal_revision integer null; + +update scheduler.scheduling_goal_analysis_satisfying_activities as sa +set goal_id = ga.goal_id, goal_revision = ga.goal_revision +from scheduler.scheduling_goal_analysis ga +where (sa.analysis_id, sa.goal_invocation_id) = (ga.analysis_id, ga.goal_invocation_id); + +update scheduler.scheduling_goal_analysis_created_activities as ca +set goal_id = ga.goal_id, goal_revision = ga.goal_revision +from scheduler.scheduling_goal_analysis ga +where (ca.analysis_id, ca.goal_invocation_id) = (ga.analysis_id, ga.goal_invocation_id); + +alter table scheduler.scheduling_goal_analysis_satisfying_activities + add constraint satisfying_activities_primary_key + primary key (analysis_id, goal_id, goal_revision, activity_id), + + add constraint satisfying_activities_references_scheduling_goal + foreign key (goal_id, goal_revision) + references scheduler.scheduling_goal_definition + on update cascade + on delete cascade, + + drop column goal_invocation_id; + +alter table scheduler.scheduling_goal_analysis_created_activities + add constraint created_activities_primary_key + primary key (analysis_id, goal_id, goal_revision, activity_id), + + add constraint created_activities_references_scheduling_goal + foreign key (goal_id, goal_revision) + references scheduler.scheduling_goal_definition + on update cascade + on delete cascade, + + drop column goal_invocation_id; + +alter table scheduler.scheduling_goal_analysis + drop constraint scheduling_goal_analysis_primary_key; + +-- delete all but the first goal invocation from analysis table if there nonuniqueness +-- between the composite key that will become the new (old) PK +delete +from scheduler.scheduling_goal_analysis +where goal_invocation_id not in (select min(goal_invocation_id) + from scheduler.scheduling_goal_analysis + group by analysis_id, goal_id, goal_revision); + +alter table scheduler.scheduling_goal_analysis + add constraint scheduling_goal_analysis_primary_key + primary key (analysis_id, goal_id, goal_revision), + + drop column goal_invocation_id; + +-- restore scheduling_specification_goals +-- delete data for new PK +-- i.e. find where (spec_id, goal_id) is not unique +-- and delete all entries except for the one with the lowest goal_invocation_id +-- so that (spec_id, goal_id) can be the new PK +-- delete from scheduler.scheduling_specification_goals a +delete +from scheduler.scheduling_specification_goals +where goal_invocation_id not in (select min(goal_invocation_id) + from scheduler.scheduling_specification_goals + group by specification_id, goal_id); + +alter table scheduler.scheduling_specification_goals + drop constraint scheduling_specification_goals_primary_key, + add constraint scheduling_specification_goals_primary_key + primary key (specification_id, goal_id), + + drop column goal_invocation_id; + +comment on column scheduler.scheduling_goal_definition.definition is e'' + 'An executable expression in the Merlin scheduling language.'; + +call migrations.mark_migration_rolled_back('9'); diff --git a/deployment/hasura/migrations/Aerie/9_goal_invocations/up.sql b/deployment/hasura/migrations/Aerie/9_goal_invocations/up.sql new file mode 100644 index 0000000000..f2d7f4d32c --- /dev/null +++ b/deployment/hasura/migrations/Aerie/9_goal_invocations/up.sql @@ -0,0 +1,90 @@ +alter table scheduler.scheduling_specification_goals + add column goal_invocation_id integer generated by default as identity, + + drop constraint scheduling_specification_goals_primary_key, + add constraint scheduling_specification_goals_primary_key + primary key (goal_invocation_id); + +comment on column scheduler.scheduling_specification_goals.goal_invocation_id is e'' + 'The id of a specific goal invocation in the specification. Primary key.'; + +-- update goal_analysis PK +alter table scheduler.scheduling_goal_analysis + add column goal_invocation_id integer null, -- temp set as nullable so we can insert, made not null by PK constraint below + + drop constraint scheduling_goal_analysis_primary_key; + +comment on column scheduler.scheduling_goal_analysis.goal_invocation_id is e'' + 'The associated goal invocation ID.'; + +update scheduler.scheduling_goal_analysis as sga +set goal_invocation_id = ssg.goal_invocation_id +from scheduler.scheduling_request as sr + join scheduler.scheduling_specification_goals as ssg + on sr.specification_id = ssg.specification_id +where sr.analysis_id = sga.analysis_id + and sga.goal_id = ssg.goal_id; + +alter table scheduler.scheduling_goal_analysis + add constraint scheduling_goal_analysis_primary_key + primary key (analysis_id, goal_invocation_id); + +-- update created_activities PK +alter table scheduler.scheduling_goal_analysis_created_activities + drop constraint created_activities_primary_key, + -- temp set as nullable so we can insert, made not null by PK constraint below + add column goal_invocation_id integer null; + +comment on column scheduler.scheduling_goal_analysis_created_activities.goal_invocation_id is e'' + 'The associated goal invocation ID from the scheduling specification.'; + +update scheduler.scheduling_goal_analysis_created_activities as sgaca +set goal_invocation_id = ssg.goal_invocation_id +from scheduler.scheduling_request as sr + join scheduler.scheduling_specification_goals as ssg + on sr.specification_id = ssg.specification_id +where sr.analysis_id = sgaca.analysis_id + and sgaca.goal_id = ssg.goal_id; + +alter table scheduler.scheduling_goal_analysis_created_activities + drop column goal_id, + drop column goal_revision, + + add constraint created_activities_primary_key + primary key (analysis_id, goal_invocation_id, activity_id), + add constraint created_activities_references_scheduling_goal_analysis + foreign key (analysis_id, goal_invocation_id) + references scheduler.scheduling_goal_analysis + on update cascade + on delete cascade; + +-- update satisfing_activities PK +alter table scheduler.scheduling_goal_analysis_satisfying_activities + drop constraint satisfying_activities_primary_key, + -- temp set as nullable so we can insert, made not null by PK constraint below + add column goal_invocation_id integer null; + +comment on column scheduler.scheduling_goal_analysis_satisfying_activities.goal_invocation_id is e'' + 'The associated goal invocation ID from the scheduling specification.'; + +update scheduler.scheduling_goal_analysis_satisfying_activities as sgasa +set goal_invocation_id = ssg.goal_invocation_id +from scheduler.scheduling_request as sr + join scheduler.scheduling_specification_goals as ssg + on sr.specification_id = ssg.specification_id +where sr.analysis_id = sgasa.analysis_id + and sgasa.goal_id = ssg.goal_id; + +alter table scheduler.scheduling_goal_analysis_satisfying_activities + drop column goal_id, + drop column goal_revision, + + add constraint satisfying_activities_primary_key + primary key (analysis_id, goal_invocation_id, activity_id), + add constraint satisfying_activities_references_scheduling_goal_analysis + foreign key (analysis_id, goal_invocation_id) + references scheduler.scheduling_goal_analysis + on update cascade + on delete cascade; + +call migrations.mark_migration_applied('9'); diff --git a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis.sql b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis.sql index c10ec48727..58cfaa1ef0 100644 --- a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis.sql +++ b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis.sql @@ -1,11 +1,12 @@ create table scheduler.scheduling_goal_analysis ( analysis_id integer not null, goal_id integer not null, + goal_invocation_id integer not null, goal_revision integer not null, satisfied boolean not null, constraint scheduling_goal_analysis_primary_key - primary key (analysis_id, goal_id, goal_revision), + primary key (analysis_id, goal_invocation_id), constraint scheduling_goal_analysis_references_scheduling_request foreign key (analysis_id) references scheduler.scheduling_request (analysis_id) @@ -19,11 +20,13 @@ create table scheduler.scheduling_goal_analysis ( ); comment on table scheduler.scheduling_goal_analysis is e'' - 'The analysis of single goal from a scheduling run.'; + 'The analysis of single goal invocation from a scheduling run.'; comment on column scheduler.scheduling_goal_analysis.analysis_id is e'' 'The associated analysis ID.'; comment on column scheduler.scheduling_goal_analysis.goal_id is e'' 'The associated goal ID.'; +comment on column scheduler.scheduling_goal_analysis.goal_invocation_id is e'' + 'The associated goal invocation ID.'; comment on column scheduler.scheduling_goal_analysis.goal_revision is e'' 'The associated version of the goal definition used.'; comment on column scheduler.scheduling_goal_analysis.satisfied is e'' diff --git a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis_created_activities.sql b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis_created_activities.sql index d8a04fd1d8..7073c0d7d2 100644 --- a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis_created_activities.sql +++ b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis_created_activities.sql @@ -1,19 +1,18 @@ create table scheduler.scheduling_goal_analysis_created_activities ( analysis_id integer not null, - goal_id integer not null, - goal_revision integer not null, + goal_invocation_id integer not null, activity_id integer not null, constraint created_activities_primary_key - primary key (analysis_id, goal_id, goal_revision, activity_id), + primary key (analysis_id, goal_invocation_id, activity_id), constraint created_activities_references_scheduling_request foreign key (analysis_id) - references scheduler.scheduling_request (analysis_id) + references scheduler.scheduling_request on update cascade on delete cascade, - constraint created_activities_references_scheduling_goal - foreign key (goal_id, goal_revision) - references scheduler.scheduling_goal_definition + constraint created_activities_references_scheduling_goal_analysis + foreign key (analysis_id, goal_invocation_id) + references scheduler.scheduling_goal_analysis on update cascade on delete cascade ); @@ -22,9 +21,7 @@ comment on table scheduler.scheduling_goal_analysis_created_activities is e'' 'The activity instances created by a scheduling run to satisfy a goal.'; comment on column scheduler.scheduling_goal_analysis_created_activities.analysis_id is e'' 'The associated analysis ID.'; -comment on column scheduler.scheduling_goal_analysis_created_activities.goal_id is e'' - 'The associated goal ID.'; -comment on column scheduler.scheduling_goal_analysis_created_activities.goal_revision is e'' - 'The associated version of the goal definition used.'; +comment on column scheduler.scheduling_goal_analysis_created_activities.goal_invocation_id is e'' + 'The associated goal invocation ID from the scheduling specification.'; comment on column scheduler.scheduling_goal_analysis_created_activities.activity_id is e'' 'The ID of an activity instance created to satisfy the associated goal.'; diff --git a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis_satisfying_activities.sql b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis_satisfying_activities.sql index 6e6754b972..306f7b466b 100644 --- a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis_satisfying_activities.sql +++ b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis_satisfying_activities.sql @@ -1,19 +1,18 @@ create table scheduler.scheduling_goal_analysis_satisfying_activities ( analysis_id integer not null, - goal_id integer not null, - goal_revision integer not null, + goal_invocation_id integer not null, activity_id integer not null, constraint satisfying_activities_primary_key - primary key (analysis_id, goal_id, goal_revision, activity_id), + primary key (analysis_id, goal_invocation_id, activity_id), constraint satisfying_activities_references_scheduling_request foreign key (analysis_id) - references scheduler.scheduling_request (analysis_id) + references scheduler.scheduling_request on update cascade on delete cascade, - constraint satisfying_activities_references_scheduling_goal - foreign key (goal_id, goal_revision) - references scheduler.scheduling_goal_definition + constraint satisfying_activities_references_scheduling_goal_analysis + foreign key (analysis_id, goal_invocation_id) + references scheduler.scheduling_goal_analysis on update cascade on delete cascade ); @@ -22,9 +21,7 @@ comment on table scheduler.scheduling_goal_analysis_satisfying_activities is e'' 'The activity instances satisfying a scheduling goal.'; comment on column scheduler.scheduling_goal_analysis_satisfying_activities.analysis_id is e'' 'The associated analysis ID.'; -comment on column scheduler.scheduling_goal_analysis_satisfying_activities.goal_id is e'' - 'The associated goal ID.'; -comment on column scheduler.scheduling_goal_analysis_satisfying_activities.goal_revision is e'' - 'The associated version of the goal definition used.'; comment on column scheduler.scheduling_goal_analysis_satisfying_activities.activity_id is e'' 'The ID of an activity instance satisfying the associated goal.'; +comment on column scheduler.scheduling_goal_analysis_satisfying_activities.goal_invocation_id is e'' + 'The associated goal invocation ID from the scheduling specification.'; diff --git a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_specification/scheduling_specification_goals.sql b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_specification/scheduling_specification_goals.sql index c11522daa2..d945ed51f7 100644 --- a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_specification/scheduling_specification_goals.sql +++ b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_specification/scheduling_specification_goals.sql @@ -1,4 +1,5 @@ create table scheduler.scheduling_specification_goals ( + goal_invocation_id integer generated by default as identity, specification_id integer not null, goal_id integer not null, goal_revision integer, -- latest is null @@ -8,7 +9,7 @@ create table scheduler.scheduling_specification_goals ( simulate_after boolean not null default true, constraint scheduling_specification_goals_primary_key - primary key (specification_id, goal_id), + primary key (goal_invocation_id), constraint scheduling_specification_goals_specification_exists foreign key (specification_id) references scheduler.scheduling_specification @@ -32,10 +33,12 @@ create table scheduler.scheduling_specification_goals ( comment on table scheduler.scheduling_specification_goals is e'' 'The scheduling goals to be executed against a given plan.'; +comment on column scheduler.scheduling_specification_goals.goal_invocation_id is e'' + 'The id of a specific goal invocation in the specification. Primary key.'; comment on column scheduler.scheduling_specification_goals.specification_id is e'' - 'The plan scheduling specification this goal is on. Half of the primary key.'; + 'The plan scheduling specification this goal is on.'; comment on column scheduler.scheduling_specification_goals.goal_id is e'' - 'The id of a specific goal in the specification. Half of the primary key.'; + 'The id of a specific goal in the specification.'; comment on column scheduler.scheduling_specification_goals.goal_revision is e'' 'The version of the goal definition to use. Leave NULL to use the latest version.'; comment on column scheduler.scheduling_specification_goals.priority is e'' From 69a324bf59459fb6516ba3eb4ca979523a3e9519 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Tue, 20 Aug 2024 11:37:06 -0700 Subject: [PATCH 021/108] Update scheduler server to handle goal invocation id --- .../aerie/scheduler/server/models/GoalId.java | 11 +- .../postgres/GetCreatedActivitiesAction.java | 16 +- .../postgres/GetGoalSatisfactionAction.java | 7 +- .../GetSatisfyingActivitiesAction.java | 15 +- .../postgres/GetSpecificationGoalsAction.java | 6 +- .../InsertCreatedActivitiesAction.java | 9 +- .../InsertGoalSatisfactionAction.java | 9 +- .../InsertSatisfyingActivitiesAction.java | 13 +- .../services/SynchronousSchedulerAgent.java | 7 +- ...va => SchedulingEdslIntegrationTests.java} | 168 +++++++++--------- 10 files changed, 147 insertions(+), 114 deletions(-) rename scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/{SchedulingIntegrationTests.java => SchedulingEdslIntegrationTests.java} (96%) diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalId.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalId.java index 70aec3c026..ec4ab2b8a6 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalId.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalId.java @@ -1,3 +1,12 @@ package gov.nasa.jpl.aerie.scheduler.server.models; -public record GoalId(long id, long revision) { } +import java.util.Optional; + +public record GoalId(long id, long revision, Optional goalInvocationId) { + public GoalId(long id, long revision) { + this(id, revision, Optional.empty()); + } + public GoalId(long id, long revision, long goalInvocationId) { + this(id, revision, Optional.of(goalInvocationId)); + } +} diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java index 0caeafdd23..10bbe8ae46 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java @@ -11,15 +11,18 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; /*package-local*/ final class GetCreatedActivitiesAction implements AutoCloseable { private static final @Language("SQL") String sql = """ select - c.goal_id, - c.goal_revision, + a.goal_id, + a.goal_revision, + c.goal_invocation_id, c.activity_id from scheduler.scheduling_goal_analysis_created_activities as c - where c.analysis_id = ? + join scheduler.scheduling_goal_analysis as a using (goal_invocation_id) + where c.analysis_id = ? and a.analysis_id = ? """; private final PreparedStatement statement; @@ -30,11 +33,16 @@ public GetCreatedActivitiesAction(final Connection connection) throws SQLExcepti public Map> get(final long analysisId) throws SQLException { this.statement.setLong(1, analysisId); + this.statement.setLong(2, analysisId); final var resultSet = this.statement.executeQuery(); final var createdActivities = new HashMap>(); while (resultSet.next()) { - final var goalId = new GoalId(resultSet.getLong("goal_id"), resultSet.getLong("goal_revision")); + final var goalId = new GoalId( + resultSet.getLong("goal_id"), + resultSet.getLong("goal_revision"), + Optional.of(resultSet.getLong("goal_invocation_id")) + ); final var activityId = new ActivityDirectiveId(resultSet.getLong("activity_id")); if (!createdActivities.containsKey(goalId)) createdActivities.put(goalId, new ArrayList<>()); diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetGoalSatisfactionAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetGoalSatisfactionAction.java index f06adcabfd..8624da2af2 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetGoalSatisfactionAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetGoalSatisfactionAction.java @@ -12,6 +12,7 @@ private final static @Language("SQL") String sql = """ select goal.goal_id, + goal.goal_invocation_id, goal.goal_revision, goal.satisfied from scheduler.scheduling_goal_analysis as goal @@ -31,7 +32,11 @@ public Map get(final long analysisId) throws SQLException { final var goals = new HashMap(); while (resultSet.next()) { goals.put( - new GoalId(resultSet.getLong("goal_id"), resultSet.getLong("goal_revision")), + new GoalId( + resultSet.getLong("goal_id"), + resultSet.getLong("goal_revision"), + resultSet.getLong("goal_invocation_id") + ), resultSet.getBoolean("satisfied") ); } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSatisfyingActivitiesAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSatisfyingActivitiesAction.java index 4254634d26..d52dfbdf47 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSatisfyingActivitiesAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSatisfyingActivitiesAction.java @@ -15,11 +15,13 @@ /*package-local*/ final class GetSatisfyingActivitiesAction implements AutoCloseable { private static final @Language("SQL") String sql = """ select - s.goal_id, - s.goal_revision, + a.goal_id, + a.goal_revision, + s.goal_invocation_id, s.activity_id from scheduler.scheduling_goal_analysis_satisfying_activities as s - where s.analysis_id = ? + join scheduler.scheduling_goal_analysis as a using (goal_invocation_id) + where s.analysis_id = ? and a.analysis_id = ? """; private final PreparedStatement statement; @@ -30,11 +32,16 @@ public GetSatisfyingActivitiesAction(final Connection connection) throws SQLExce public Map> get(final long analysisId) throws SQLException { this.statement.setLong(1, analysisId); + this.statement.setLong(2, analysisId); final var resultSet = this.statement.executeQuery(); final var satisfyingActivities = new HashMap>(); while (resultSet.next()) { - final var goalId = new GoalId(resultSet.getLong("goal_id"), resultSet.getLong("goal_revision")); + final var goalId = new GoalId( + resultSet.getLong("goal_id"), + resultSet.getLong("goal_revision"), + resultSet.getLong("goal_invocation_id") + ); final var activityId = new ActivityDirectiveId(resultSet.getLong("activity_id")); satisfyingActivities diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java index 4753859b2a..cd98ccb6d6 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java @@ -13,10 +13,11 @@ /*package-local*/ final class GetSpecificationGoalsAction implements AutoCloseable { private final @Language("SQL") String sql = """ - select s.goal_id, gd.revision, gm.name, gd.definition, s.simulate_after + select s.goal_id, gd.revision, gm.name, gd.definition, s.goal_invocation_id, s.simulate_after from scheduler.scheduling_specification_goals s left join scheduler.scheduling_goal_definition gd using (goal_id) left join scheduler.scheduling_goal_metadata gm on s.goal_id = gm.id + left join merlin.uploaded_file f on gd.uploaded_jar_id = f.id where s.specification_id = ? and s.enabled and ((s.goal_revision is not null and s.goal_revision = gd.revision) @@ -40,11 +41,12 @@ public List get(final long specificationId) throws SQLException { final var goals = new ArrayList(); while (resultSet.next()) { final var id = resultSet.getLong("goal_id"); + final var goalInvocationId = resultSet.getLong("goal_invocation_id"); final var revision = resultSet.getLong("revision"); final var name = resultSet.getString("name"); final var definition = resultSet.getString("definition"); final var simulateAfter = resultSet.getBoolean("simulate_after"); - goals.add(new GoalRecord(new GoalId(id, revision), name, new GoalSource(definition), simulateAfter)); + goals.add(new GoalRecord(new GoalId(id, revision, goalInvocationId), name, new GoalSource(definition), simulateAfter)); } return goals; diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertCreatedActivitiesAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertCreatedActivitiesAction.java index 65770d1da5..87e5205d95 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertCreatedActivitiesAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertCreatedActivitiesAction.java @@ -13,8 +13,8 @@ /*package-local*/ final class InsertCreatedActivitiesAction implements AutoCloseable { private static final @Language("SQL") String sql = """ - insert into scheduler.scheduling_goal_analysis_created_activities (analysis_id, goal_id, goal_revision, activity_id) - values (?, ?, ?, ?) + insert into scheduler.scheduling_goal_analysis_created_activities (analysis_id, goal_invocation_id, activity_id) + values (?, ?, ?) """; private final PreparedStatement statement; @@ -31,9 +31,8 @@ public void apply( final var goal = entry.getKey(); for (final var activityId : entry.getValue()) { this.statement.setLong(1, analysisId); - this.statement.setLong(2, goal.id()); - this.statement.setLong(3, goal.revision()); - this.statement.setLong(4, activityId.id()); + this.statement.setLong(2, goal.goalInvocationId().get()); + this.statement.setLong(3, activityId.id()); this.statement.addBatch(); } } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertGoalSatisfactionAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertGoalSatisfactionAction.java index 76615d7fef..0a3083ea54 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertGoalSatisfactionAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertGoalSatisfactionAction.java @@ -11,8 +11,8 @@ /*package-local*/ final class InsertGoalSatisfactionAction implements AutoCloseable { private static final @Language("SQL") String sql = """ - insert into scheduler.scheduling_goal_analysis (analysis_id, goal_id, goal_revision, satisfied) - values (?, ?, ?, ?) + insert into scheduler.scheduling_goal_analysis (analysis_id, goal_id, goal_invocation_id, goal_revision, satisfied) + values (?, ?, ?, ?, ?) """; private final PreparedStatement statement; @@ -26,8 +26,9 @@ public void apply(final long analysisId, final Map goalSatisfac final var goal = entry.getKey(); this.statement.setLong(1, analysisId); this.statement.setLong(2, goal.id()); - this.statement.setLong(3, goal.revision()); - this.statement.setBoolean(4, entry.getValue()); + this.statement.setLong(3, goal.goalInvocationId().get()); + this.statement.setLong(4, goal.revision()); + this.statement.setBoolean(5, entry.getValue()); this.statement.addBatch(); } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertSatisfyingActivitiesAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertSatisfyingActivitiesAction.java index 0b6b9a5f79..3fa7556f48 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertSatisfyingActivitiesAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/InsertSatisfyingActivitiesAction.java @@ -13,8 +13,12 @@ /*package-local*/ final class InsertSatisfyingActivitiesAction implements AutoCloseable { private static final @Language("SQL") String sql = """ - insert into scheduler.scheduling_goal_analysis_satisfying_activities (analysis_id, goal_id, goal_revision, activity_id) - values (?, ?, ?, ?) + insert into scheduler.scheduling_goal_analysis_satisfying_activities ( + analysis_id, + goal_invocation_id, + activity_id + ) + values (?, ?, ?) """; private final PreparedStatement statement; @@ -31,9 +35,8 @@ public void apply( final var goal = entry.getKey(); for (final var activityId : entry.getValue()) { this.statement.setLong(1, analysisId); - this.statement.setLong(2, goal.id()); - this.statement.setLong(3, goal.revision()); - this.statement.setLong(4, activityId.id()); + this.statement.setLong(2, goal.goalInvocationId().get()); + this.statement.setLong(3, activityId.id()); this.statement.addBatch(); } } diff --git a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java index 54df92b893..57fbdad741 100644 --- a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java +++ b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java @@ -50,7 +50,6 @@ import gov.nasa.jpl.aerie.scheduler.server.models.ExternalProfiles; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; -import gov.nasa.jpl.aerie.scheduler.server.models.GoalSource; import gov.nasa.jpl.aerie.scheduler.server.models.MerlinPlan; import gov.nasa.jpl.aerie.scheduler.server.models.PlanId; import gov.nasa.jpl.aerie.scheduler.server.models.PlanMetadata; @@ -192,7 +191,7 @@ public void schedule( final var result = compileGoalDefinition( merlinDatabaseService, planMetadata.planId(), - goalRecord.definition(), + goalRecord.definition().source(), schedulingDSLCompilationService, externalProfiles.resourceTypes()); if (result instanceof SchedulingDSLCompilationService.SchedulingDSLCompilationResult.Success r) { @@ -342,14 +341,14 @@ private Optional storeSimulationResults( private static SchedulingDSLCompilationService.SchedulingDSLCompilationResult compileGoalDefinition( final MerlinDatabaseService.ReaderRole merlinDatabaseService, final PlanId planId, - final GoalSource goalDefinition, + final String source, final SchedulingDSLCompilationService schedulingDSLCompilationService, final Collection additionalResourceTypes) { return schedulingDSLCompilationService.compileSchedulingGoalDSL( merlinDatabaseService, planId, - goalDefinition.source(), + source, additionalResourceTypes ); } diff --git a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingIntegrationTests.java b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java similarity index 96% rename from scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingIntegrationTests.java rename to scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java index 49a7795655..bd5c774f8d 100644 --- a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingIntegrationTests.java +++ b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java @@ -67,7 +67,7 @@ import org.junit.jupiter.params.provider.MethodSource; @TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class SchedulingIntegrationTests { +public class SchedulingEdslIntegrationTests { public static final PlanningHorizon PLANNING_HORIZON = new PlanningHorizon( TimeUtility.fromDOY("2021-001T00:00:00"), @@ -75,8 +75,8 @@ public class SchedulingIntegrationTests { private record MissionModelDescription(String name, Map config, Path libPath) {} - private record SchedulingGoal(GoalId goalId, String definition, boolean enabled, boolean simulateAfter) { - public SchedulingGoal(GoalId goalId, String definition, boolean enabled) { + private record EdslGoal(GoalId goalId, String definition, boolean enabled, boolean simulateAfter) { + public EdslGoal(GoalId goalId, String definition, boolean enabled) { this(goalId, definition, enabled, true); } } @@ -107,7 +107,7 @@ void testEmptyPlanEmptySpecification() { @Test void testEmptyPlanSimpleRecurrenceGoal() { - final var results = runScheduler(BANANANATION, List.of(), List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + final var results = runScheduler(BANANANATION, List.of(), List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({ peelDirection: "fromStem", @@ -136,7 +136,7 @@ export default () => Goal.ActivityRecurrenceGoal({ @Test void testRecurrenceGoalNegative() { try { - runScheduler(BANANANATION, List.of(), List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + runScheduler(BANANANATION, List.of(), List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({ peelDirection: "fromStem", @@ -156,7 +156,7 @@ export default () => Goal.ActivityRecurrenceGoal({ @Test void testEmptyPlanDurationCardinalityGoal() { - final var results = runScheduler(BANANANATION, List.of(), List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + final var results = runScheduler(BANANANATION, List.of(), List.of(new EdslGoal(new GoalId(0L, 0L), """ export default function myGoal() { return Goal.CardinalityGoal({ activityTemplate: ActivityTemplates.GrowBanana({ @@ -197,7 +197,7 @@ export default function myGoal() { void testEmptyPlanOccurrenceCardinalityGoal() { final var results = runScheduler(BANANANATION, List.of(), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default function myGoal() { return Goal.CardinalityGoal({ activityTemplate: ActivityTemplates.GrowBanana({ @@ -240,7 +240,7 @@ void testEmptyPlanOccurrenceUnitaryGoalTimeInstant() { final var results = runScheduler( BANANANATION, List.of(), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: Temporal.Instant.from("2021-01-01T05:00:00.000Z"), activityTemplate: (span) => ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -275,7 +275,7 @@ void testEmptyPlanOccurrenceUnitaryGoalTimeInterval() { final var results = runScheduler( BANANANATION, List.of(), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: Interval.Between(Temporal.Instant.from("2021-01-01T05:00:00.000Z"), Temporal.Instant.from("2021-01-01T10:00:00.000Z"), Inclusivity.Inclusive, Inclusivity.Exclusive), activityTemplate: (span) => ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -316,7 +316,7 @@ void testSingleActivityPlanSimpleRecurrenceGoal() { Map.of("biteSize", SerializedValue.of(1)), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), interval: Temporal.Duration.from({days: 1}) @@ -357,13 +357,13 @@ void testSingleActivityPlanSimpleCoexistenceGoalWithValueAtParams() { BANANANATION, List.of(new ActivityDirective( Duration.ZERO, - "GrowBanana", - Map.of( - "quantity", SerializedValue.of(3), - "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), - null, - true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + "GrowBanana", + Map.of( + "quantity", SerializedValue.of(3), + "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), + null, + true)), + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: (growBananaActivity) => ActivityTemplates.ChangeProducer({producer: Discrete.Resource("/producer").valueAt(growBananaActivity.span().starts())}), @@ -420,7 +420,7 @@ void testCoexistencePartialAct() { true ) ), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.BiteBanana), activityFinder: ActivityExpression.ofType(ActivityTypes.GrowBanana), @@ -467,7 +467,7 @@ void testBugValueAt() { "producer", SerializedValue.of("Company")), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: Real.Resource("/fruit").greaterThan(4.0), activityTemplate: interval => ActivityTemplates.ChangeProducer({producer: Discrete.Resource("/producer").valueAt(Spans.FromInterval(interval).starts())}), @@ -513,7 +513,7 @@ void testCoexistenceGoalWithAnchors() { true ) ), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ persistentAnchor: PersistentTimeAnchor.START, forEach: ActivityExpression.ofType(ActivityTypes.BiteBanana), @@ -548,7 +548,7 @@ void testCoexistenceGoalWithAnchorsCreation() { true ) ), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ persistentAnchor: PersistentTimeAnchor.START, forEach: ActivityExpression.ofType(ActivityTypes.BiteBanana), @@ -620,7 +620,7 @@ void testCoexistencePartialActWithParameter() { true ) ), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.BiteBanana), activityFinder: ActivityExpression.build(ActivityTypes.GrowBanana, {quantity: 1}), @@ -699,7 +699,7 @@ void testRecurrenceWithActivityFinder() { null, true) ), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.GrowBanana({quantity: 2, growingDuration: Temporal.Duration.from({seconds:1})}), activityFinder: ActivityExpression.build(ActivityTypes.GrowBanana, {quantity: 2}), @@ -739,8 +739,8 @@ void testCardinalityGoalWithActivityFinder() { "growingDuration", SerializedValue.of(Duration.of(5, SECONDS).in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), - """ + List.of(new EdslGoal(new GoalId(0L, 0L), + """ export default function myGoal() { return Goal.CardinalityGoal({ activityFinder: ActivityExpression.ofType(ActivityTypes.GrowBanana), @@ -777,7 +777,7 @@ void testSingleActivityPlanSimpleCoexistenceGoalWithFunctionalParameters() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: (growBananaActivity) => ActivityTemplates.PickBanana({quantity: growBananaActivity.parameters.quantity}), @@ -834,7 +834,7 @@ void testSingleActivityPlanSimpleCoexistenceGoalWithWindowReference() { true ) ), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: Real.Resource("/fruit").lessThan(4), activityTemplate: (interval) => ActivityTemplates.GrowBanana({quantity: 10, growingDuration: interval.duration() }), @@ -883,7 +883,7 @@ void testSingleActivityPlanSimpleCoexistenceGoal() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -932,7 +932,7 @@ void testSingleActivityPlanSimpleCoexistenceGoal_constrainEndTime() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: (span) => ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -985,7 +985,7 @@ void testSingleActivityPlanSimpleCoexistenceGoal_AllenBefore() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: (span) => ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1039,7 +1039,7 @@ void testSingleActivityPlanSimpleCoexistenceGoal_AllenEquals() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: (span) => ActivityTemplates.DurationParameterActivity({duration: Temporal.Duration.from({ hours : 1})}), @@ -1096,7 +1096,7 @@ void testSingleActivityPlanSimpleCoexistenceGoal_AllenMeets() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: (span) => ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1149,7 +1149,7 @@ void testSingleActivityPlanSimpleCoexistenceGoal_AllenOverlaps() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: (span) => ActivityTemplates.DurationParameterActivity({duration: Temporal.Duration.from({ hours : 1})}), @@ -1200,7 +1200,7 @@ void testSingleActivityPlanSimpleCoexistenceGoal_AllenContains() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: (span) => ActivityTemplates.DurationParameterActivity({duration: Temporal.Duration.from({ minutes : 50})}), @@ -1260,7 +1260,7 @@ void testSingleActivityPlanSimpleCoexistenceGoal_AllenStarts() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: (span) => ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1315,7 +1315,7 @@ void testSingleActivityPlanSimpleCoexistenceGoal_AllenFinishesAt() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: (span) => ActivityTemplates.DurationParameterActivity({duration: Temporal.Duration.from({ minutes : 50})}), @@ -1369,7 +1369,7 @@ void testSingleActivityPlanSimpleCoexistenceGoal_AllenFinishesWithin() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: (span) => ActivityTemplates.DurationParameterActivity({duration: Temporal.Duration.from({ minutes : 50})}), @@ -1428,7 +1428,7 @@ void testStateCoexistenceGoal_greaterThan() { "PickBanana", Map.of("quantity", SerializedValue.of(100)), null, - true)), List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + true)), List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1472,7 +1472,7 @@ void testLessThan() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1511,7 +1511,7 @@ void testLinear_atChangePoints() { Map.of("biteSize", SerializedValue.of(1.0)), null, true) - ), List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + ), List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1548,7 +1548,7 @@ void testLinear_interpolated() { null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromTip"}), @@ -1594,7 +1594,7 @@ void testEqualTo_satsified() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1639,7 +1639,7 @@ void testEqualTo_neverSatisfied() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1678,7 +1678,7 @@ void testBigCoexistence(){ final var results = runScheduler( BANANANATION, onePickEveryTenMinutes(PLANNING_HORIZON.getHor()), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1714,7 +1714,7 @@ void testNotEqualTo_satisfied() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1759,7 +1759,7 @@ void testBetweenInTermsOfAnd() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1807,7 +1807,7 @@ void testWindowsOr() { "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1842,7 +1842,7 @@ void testWindowsTransition() { "ChangeProducer", Map.of(), null, - true)), List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + true)), List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1871,7 +1871,7 @@ void testWindowsTransition_unsatisfied() { "ChangeProducer", Map.of("producer", SerializedValue.of("Fyffes")), null, - true)), List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + true)), List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1899,7 +1899,7 @@ void testExternalResource() { final var results = runScheduler( BANANANATION, List.of(), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -1963,7 +1963,7 @@ void testApplyWhen() { null, true) ), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.ChangeProducer({producer: "Morpheus"}), interval: Temporal.Duration.from({ hours : 24 }) @@ -1989,7 +1989,7 @@ void testGlobalSchedulingConditions_conditionNeverOccurs() { final var results = runScheduler( BANANANATION, List.of(), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.ChangeProducer({producer: "Morpheus"}), interval: Temporal.Duration.from({days: 1}) @@ -2015,7 +2015,7 @@ void testGlobalSchedulingConditions_conditionAlwaysTrue() { final var results = runScheduler( BANANANATION, List.of(), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.ChangeProducer({producer: "Morpheus"}), interval: Temporal.Duration.from({days: 1}) @@ -2045,7 +2045,7 @@ void testGlobalSchedulingConditions_conditionSometimesTrue() { null, true) ), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.ChangeProducer({producer: "Morpheus"}), interval: Temporal.Duration.from({days: 1}) @@ -2122,7 +2122,7 @@ export default function myCondition() { private SchedulingRunResults runScheduler( final MissionModelDescription desc, final List plannedActivities, - final Iterable goals, + final Iterable goals, final PlanningHorizon planningHorizon){ return runScheduler(desc, plannedActivities, goals, planningHorizon, 30); } @@ -2130,7 +2130,7 @@ private SchedulingRunResults runScheduler( private SchedulingRunResults runScheduler( final MissionModelDescription desc, final List plannedActivities, - final Iterable goals, + final Iterable goals, final PlanningHorizon planningHorizon, final int cachedEngineStoreCapacity ) @@ -2146,7 +2146,7 @@ private SchedulingRunResults runScheduler( private SchedulingRunResults runScheduler( final MissionModelDescription desc, final Map plannedActivities, - final Iterable goals, + final Iterable goals, final PlanningHorizon planningHorizon ) { @@ -2156,7 +2156,7 @@ private SchedulingRunResults runScheduler( private SchedulingRunResults runScheduler( final MissionModelDescription desc, final List plannedActivities, - final Iterable goals, + final Iterable goals, final List globalSchedulingConditions, final PlanningHorizon planningHorizon ){ @@ -2166,7 +2166,7 @@ private SchedulingRunResults runScheduler( private SchedulingRunResults runScheduler( final MissionModelDescription desc, final List plannedActivities, - final Iterable goals, + final Iterable goals, final List globalSchedulingConditions, final PlanningHorizon planningHorizon, final Optional externalProfiles @@ -2182,7 +2182,7 @@ private SchedulingRunResults runScheduler( private SchedulingRunResults runScheduler( final MissionModelDescription desc, final Map plannedActivities, - final Iterable goals, + final Iterable goals, final List globalSchedulingConditions, final PlanningHorizon planningHorizon, final Optional externalProfiles, @@ -2300,7 +2300,7 @@ void testAndFailure(){ "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CardinalityGoal({ activityTemplate: ActivityTemplates.GrowBanana({ @@ -2361,7 +2361,7 @@ void testOrFailure(){ "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CardinalityGoal({ activityTemplate: ActivityTemplates.GrowBanana({ @@ -2413,7 +2413,7 @@ void testOr(){ "growingDuration", SerializedValue.of(growBananaDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.CoexistenceGoal({ activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -2469,7 +2469,7 @@ export default function myGoal() { Map.of(), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), goalDefinition, true)), + List.of(new EdslGoal(new GoalId(0L, 0L), goalDefinition, true)), planningHorizon); final var planByActivityType = partitionByActivityType(results.updatedPlan()); final var biteBanana = planByActivityType.get("BiteBanana").stream().map((bb) -> bb.startOffset()).toList(); @@ -2499,7 +2499,7 @@ export default function myGoal() { Map.of(), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), goalDefinition, true)), + List.of(new EdslGoal(new GoalId(0L, 0L), goalDefinition, true)), List.of(), planningHorizon); final var planByActivityType = partitionByActivityType(results.updatedPlan()); @@ -2521,7 +2521,7 @@ void testDurationParameter() { final var results = runScheduler( BANANANATION, List.of(), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default function myGoal() { return Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.DurationParameterActivity({ @@ -2543,7 +2543,7 @@ void testUnfinishedActivity(){ final var results = runScheduler( BANANANATION, List.of(), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => { return Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.parent({ @@ -2567,7 +2567,7 @@ public void testBugDurationInMicroseconds(){ final var results = runScheduler( BANANANATION, List.of(), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.BakeBananaBread({ temperature: 325.0, tbSugar: 2, glutenFree: false }), @@ -2578,7 +2578,7 @@ export default (): Goal => runScheduler( BANANANATION, results.updatedPlan.stream().toList(), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.BakeBananaBread({ temperature: 325.0, tbSugar: 2, glutenFree: false }), @@ -2599,7 +2599,7 @@ void test_inf_loop(){ Map.of("biteSize", SerializedValue.of(10)), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default (): Goal => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.BakeBananaBread({ temperature: 325.0, tbSugar: 2, glutenFree: false }), @@ -2660,7 +2660,7 @@ void testRelativeActivityPlanZeroStartOffsetEnd() { "duration", SerializedValue.of(activityDuration.in(Duration.MICROSECONDS))), new ActivityDirectiveId(2L), false)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -2744,7 +2744,7 @@ void testRelativeActivityPlanZeroStartOffsetStart() { "duration", SerializedValue.of(activityDuration.in(Duration.MICROSECONDS))), new ActivityDirectiveId(2L), false)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -2820,7 +2820,7 @@ void testRelativeActivityPlanNegativeStartOffsetStart() { "growingDuration", SerializedValue.of(activityDuration.in(Duration.MICROSECONDS))), new ActivityDirectiveId(1L), true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -2894,7 +2894,7 @@ void testRelativeActivityPlanPositiveStartOffsetStart() { "quantity", SerializedValue.of(1)), new ActivityDirectiveId(1L), true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.PickBanana), activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -2965,7 +2965,7 @@ void testJustAfter(String timepoint, Duration resultingStartTime) { "growingDuration", SerializedValue.of(activityDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default function(){ TimingConstraint.defaultPadding = Temporal.Duration.from({milliseconds:1}) return Goal.CoexistenceGoal({ @@ -3035,7 +3035,7 @@ void testJustBefore(String timepoint, Duration resultingStartTime) { "growingDuration", SerializedValue.of(activityDuration.in(Duration.MICROSECONDS))), null, true)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -3106,7 +3106,7 @@ void testRelativeActivityPlanPositiveEndOffsetEnd() { "growingDuration", SerializedValue.of(activityDuration.in(Duration.MICROSECONDS))), new ActivityDirectiveId(1L), false)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -3178,7 +3178,7 @@ void testDontScheduleFromOutsidePlanBounds(){ "growingDuration", SerializedValue.of(activityDuration.in(Duration.MICROSECONDS))), null, false)), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -3246,7 +3246,7 @@ void testOptionalSimulationAfterGoal_staleResources() { true) ), List.of( - new SchedulingGoal(new GoalId(0L, 0L), """ + new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: Real.Resource("/fruit").greaterThan(4), activityTemplate: ActivityTemplates.DownloadBanana({connection: "DSL"}), @@ -3254,7 +3254,7 @@ export default () => Goal.CoexistenceGoal({ }) """, true, config.getKey() ), - new SchedulingGoal(new GoalId(1L, 0L), """ + new EdslGoal(new GoalId(1L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: Real.Resource("/fruit").greaterThan(5), activityTemplate: ActivityTemplates.BananaNap(), @@ -3303,7 +3303,7 @@ void daemonTaskTest(){ null, true) ), - List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.ofType(ActivityTypes.ZeroDurationUncontrollableActivity), activityTemplate: ActivityTemplates.DaemonCheckerActivity({ @@ -3338,7 +3338,7 @@ export default () => Goal.CoexistenceGoal({ */ @Test void testEmptyPlanMinimalMissionModelSimpleRecurrenceGoal() { - runScheduler(MINIMAL_MISSION_MODEL, List.of(), List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + runScheduler(MINIMAL_MISSION_MODEL, List.of(), List.of(new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.ActivityRecurrenceGoal({ activityTemplate: ActivityTemplates.SingleActivity(), interval: Temporal.Duration.from({ milliseconds: 24 * 60 * 60 * 1000 }) @@ -3402,7 +3402,7 @@ void testForEachWithActivityArguments() { true) ), List.of( - new SchedulingGoal(new GoalId(0L, 0L), """ + new EdslGoal(new GoalId(0L, 0L), """ export default () => Goal.CoexistenceGoal({ forEach: ActivityExpression.build(ActivityTypes.GrowBanana, {quantity: 1}), activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), @@ -3428,7 +3428,7 @@ export default () => Goal.CoexistenceGoal({ @Test void testListOfListParam() { - final var results = runScheduler(FOO, List.of(), List.of(new SchedulingGoal(new GoalId(0L, 0L), """ + final var results = runScheduler(FOO, List.of(), List.of(new EdslGoal(new GoalId(0L, 0L), """ export default function myGoal() { return Goal.CardinalityGoal({ activityTemplate: ActivityTemplates.foo({ From 1e7019471b0f8d2be685cc7e64aa6468e8c36d9a Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Wed, 7 Aug 2024 06:52:31 -0700 Subject: [PATCH 022/108] Implement Procedural Scheduling --- procedural/build.gradle | 4 + procedural/scheduling/build.gradle | 22 ++++ .../procedural/scheduling/ProcedureMapper.kt | 10 ++ .../scheduling/plan/EditablePlan.kt | 4 +- .../scheduling/plan/NewDirective.kt | 31 +++-- .../scheduling/simulation/PauseBehavior.kt | 44 +++++-- .../scheduling/simulation/SimulateOptions.kt | 4 +- scheduler-driver/build.gradle | 9 ++ .../jpl/aerie/scheduler/ProcedureLoader.java | 66 ++++++++++ .../jpl/aerie/scheduler/goals/Procedure.java | 91 ++++++++++++++ .../scheduler/solver/PrioritySolver.java | 6 +- .../nasa/jpl/aerie/scheduler/plan/Commit.kt | 7 ++ .../scheduler/plan/InMemoryEditablePlan.kt | 111 ++++++++++++++++ ...rlinToProcedureSimulationResultsAdapter.kt | 118 ++++++++++++++++++ .../plan/SchedulerToProcedurePlanAdapter.kt | 48 +++++++ scheduler-server/build.gradle | 1 + .../scheduler/server/models/GoalRecord.java | 5 +- .../scheduler/server/models/GoalType.java | 9 ++ .../server/models/SchedulingDSL.java | 7 ++ .../postgres/GetSpecificationGoalsAction.java | 15 ++- .../server/remotes/postgres/GoalBuilder.java | 5 + .../GraphQLMerlinDatabaseService.java | 37 +++--- .../services/SynchronousSchedulerAgent.java | 60 ++++++--- .../SchedulingEdslIntegrationTests.java | 3 +- 24 files changed, 655 insertions(+), 62 deletions(-) create mode 100644 procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt create mode 100644 scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/ProcedureLoader.java create mode 100644 scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java create mode 100644 scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/Commit.kt create mode 100644 scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt create mode 100644 scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt create mode 100644 scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt create mode 100644 scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java diff --git a/procedural/build.gradle b/procedural/build.gradle index 65061f0d89..b7c8dfd9ce 100644 --- a/procedural/build.gradle +++ b/procedural/build.gradle @@ -3,6 +3,10 @@ plugins { id 'org.jetbrains.dokka' version '1.9.10' } +subprojects { + group = "gov.nasa.ammos.aerie.procedural" +} + repositories { mavenCentral() } diff --git a/procedural/scheduling/build.gradle b/procedural/scheduling/build.gradle index 08949fed2a..08d4ea15b3 100644 --- a/procedural/scheduling/build.gradle +++ b/procedural/scheduling/build.gradle @@ -6,6 +6,7 @@ plugins { id "org.jetbrains.kotlin.jvm" version "1.9.22" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' + id 'maven-publish' } repositories { @@ -52,3 +53,24 @@ dokkaHtmlPartial.configure { } } +publishing { + publications { + library(MavenPublication) { + version = findProperty("publishing.version") + from components.java + } + } + + publishing { + repositories { + maven { + name = findProperty("publishing.name") + url = findProperty("publishing.url") + credentials { + username = System.getenv(findProperty("publishing.usernameEnvironmentVariable")) + password = System.getenv(findProperty("publishing.passwordEnvironmentVariable")) + } + } + } + } +} diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt new file mode 100644 index 0000000000..6e5c9e3e16 --- /dev/null +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt @@ -0,0 +1,10 @@ +package gov.nasa.ammos.aerie.procedural.scheduling + +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue +import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema + +interface ProcedureMapper { + fun valueSchema(): ValueSchema + fun serialize(procedure: T): SerializedValue + fun deserialize(arguments: SerializedValue): T +} diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/EditablePlan.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/EditablePlan.kt index 1d6f6b002c..9d8837729a 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/EditablePlan.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/EditablePlan.kt @@ -6,6 +6,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults +import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId /** A plan representation that can be edited and simulated. */ interface EditablePlan: Plan { @@ -18,7 +19,7 @@ interface EditablePlan: Plan { * @param directive a directive without a directive id. * @return a long, the directive id this activity will have. */ - fun create(directive: NewDirective): Long + fun create(directive: NewDirective): ActivityDirectiveId /** A simplified version of [create] with minimal arguments. */ fun create( @@ -32,7 +33,6 @@ interface EditablePlan: Plan { start )) - /** Commit plan edits, making them final. */ fun commit() diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt index 009147de56..e81dbe4aae 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt @@ -28,17 +28,22 @@ data class NewDirective( * @param id The id for the new directive. * @param parent The activity this activity is anchored to, if applicable. */ - fun resolve(id: ActivityDirectiveId, parent: Directive<*>?): Directive { - if (start is DirectiveStart.Anchor) { - if (parent == null) throw IllegalArgumentException("Parent must provided when anchor is not null") - start.estimatedStart = parent.startTime + start.offset - } - return Directive( - inner, - name, - id, - type, - start - ) - } + fun resolve(id: ActivityDirectiveId, parent: Directive<*>?) = Directive( + inner, + name, + id, + type, + when (start) { + is DirectiveStart.Absolute -> start + is DirectiveStart.Anchor -> { + if (parent == null) throw IllegalArgumentException("Parent must provided when anchor is not null") + DirectiveStart.Anchor( + parent.id, + start.offset, + start.anchorPoint, + parent.startTime + start.offset + ) + } + } + ) } diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/PauseBehavior.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/PauseBehavior.kt index ed436a5664..b3ce6628b0 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/PauseBehavior.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/PauseBehavior.kt @@ -1,26 +1,54 @@ package gov.nasa.ammos.aerie.procedural.scheduling.simulation import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan /** Behavior for when the simulation should pause. */ sealed interface PauseBehavior { + /** + * @suppress + * + * Currently this function is to convert the pause behavior into a concrete time, + * but this will need to be refactored to a different way of pausing to support + * pausing after activities. + */ + fun resolve(plan: Plan): Duration + /** Pause after a given amount of time has elapsed. */ - data class AfterDuration(/***/ val dur: Duration): PauseBehavior + data class AfterDuration(/***/ val dur: Duration): PauseBehavior { + override fun resolve(plan: Plan) = dur + } - /** Pause after a specific activity directive has finished. */ - data class AfterActivity(/** Id of the directive to wait for. */ val directive: Long): PauseBehavior +// /** Pause after a specific activity directive has finished. */ +// data class AfterActivity(/** Id of the directive to wait for. */ val directive: Long): PauseBehavior - /** Pause after all edits that were not included in the previous sim are simulated. */ - data object AfterNewEdits: PauseBehavior +// /** Pause after all edits that were not included in the previous sim are simulated. */ +// data object AfterNewEdits: PauseBehavior /** Do not pause; continue to the end of the plan. */ - data object AtEnd: PauseBehavior + data object AtEnd: PauseBehavior { + override fun resolve(plan: Plan) = plan.totalBounds().end + } /** Pause at the earliest of a list of possible pause points. */ - data class EarliestOf(/***/ val pausePoints: List): PauseBehavior + data class EarliestOf(/***/ val pausePoints: List): PauseBehavior { + init { + assert(pausePoints.isNotEmpty()) + } + + /***/ constructor(vararg pausePoints: PauseBehavior): this(pausePoints.asList()) + override fun resolve(plan: Plan) = pausePoints.minOf { it.resolve(plan) } + } /** Pause at the latest of a list of possible pause points. */ - data class LatestOf(/***/ val pausePoints: List): PauseBehavior + data class LatestOf(/***/ val pausePoints: List): PauseBehavior { + init { + assert(pausePoints.isNotEmpty()) + } + + /***/ constructor(vararg pausePoints: PauseBehavior): this(pausePoints.asList()) + override fun resolve(plan: Plan) = pausePoints.maxOf { it.resolve(plan) } + } // very hard! // data class OnCondition(val condition: ???): PauseBehavior diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/SimulateOptions.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/SimulateOptions.kt index 96bf40b606..74361879c7 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/SimulateOptions.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/SimulateOptions.kt @@ -5,7 +5,5 @@ package gov.nasa.ammos.aerie.procedural.scheduling.simulation // The following two options will be uncommented when checkpoint simulation is released. // val checkPointGeneration: CheckpointGeneration = CheckpointGeneration.None, // val checkpointRetention: CheckpointRetention = CheckpointRetention.All, - - // TODO This option will be uncommented before MVP release. (see `feat/procedural-scheduling` branch) -// val pause: PauseBehavior = PauseBehavior.AtEnd, + val pause: PauseBehavior = PauseBehavior.AtEnd, ) diff --git a/scheduler-driver/build.gradle b/scheduler-driver/build.gradle index 41c84dac59..fa75c37b39 100644 --- a/scheduler-driver/build.gradle +++ b/scheduler-driver/build.gradle @@ -2,6 +2,7 @@ plugins { id 'java-library' id 'jacoco' id 'maven-publish' + id 'org.jetbrains.kotlin.jvm' version '1.9.23' } java { @@ -39,12 +40,17 @@ dependencies { implementation project(':merlin-framework') implementation project(':type-utils') + + implementation project(':procedural:scheduling') + implementation project(':procedural:timeline') + testImplementation project(':merlin-framework-junit') testImplementation project(':constraints') testImplementation project(':examples:banananation') testImplementation project(':examples:foo-missionmodel') testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' testImplementation 'com.google.guava:guava-testlib:32.1.2-jre' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } @@ -70,3 +76,6 @@ publishing { } } } +repositories { + mavenCentral() +} diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/ProcedureLoader.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/ProcedureLoader.java new file mode 100644 index 0000000000..50a46ec1e8 --- /dev/null +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/ProcedureLoader.java @@ -0,0 +1,66 @@ +package gov.nasa.jpl.aerie.scheduler; + +import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; +import gov.nasa.ammos.aerie.procedural.scheduling.ProcedureMapper; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.Objects; +import java.util.jar.JarFile; + +public final class ProcedureLoader { + public static ProcedureMapper loadProcedure(final Path path) + throws ProcedureLoadException + { + final var className = getImplementingClassName(path); + final var classLoader = new URLClassLoader(new URL[] {pathToUrl(path)}); + + try { + final var pluginClass$ = classLoader.loadClass(className); + if (!ProcedureMapper.class.isAssignableFrom(pluginClass$)) { + throw new ProcedureLoadException(path); + } + + return (ProcedureMapper) pluginClass$.getConstructor().newInstance(); + } catch (final ReflectiveOperationException ex) { + throw new ProcedureLoadException(path, ex); + } + } + + private static String getImplementingClassName(final Path jarPath) + throws ProcedureLoadException { + try (final var jarFile = new JarFile(jarPath.toFile())) { + return Objects.requireNonNull(jarFile.getManifest().getMainAttributes().getValue("Main-Class")); + } catch (final IOException ex) { + throw new ProcedureLoadException(jarPath, ex); + } + } + + private static URL pathToUrl(final Path path) { + try { + return path.toUri().toURL(); + } catch (final MalformedURLException ex) { + // This exception only happens if there is no URL protocol handler available to represent a Path. + // This is highly unexpected, and indicates a fundamental problem with the system environment. + throw new Error(ex); + } + } + + public static class ProcedureLoadException extends Exception { + private ProcedureLoadException(final Path path) { + this(path, null); + } + + private ProcedureLoadException(final Path path, final Throwable cause) { + super( + String.format( + "No implementation found for `%s` at path `%s`", + ProcedureMapper.class.getSimpleName(), + path), + cause); + } + } +} diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java new file mode 100644 index 0000000000..fe0b5ca0fa --- /dev/null +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java @@ -0,0 +1,91 @@ +package gov.nasa.jpl.aerie.scheduler.goals; + +import gov.nasa.jpl.aerie.merlin.driver.MissionModel; +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; +import gov.nasa.ammos.aerie.procedural.scheduling.ProcedureMapper; +import gov.nasa.ammos.aerie.procedural.scheduling.plan.Edit; +import gov.nasa.jpl.aerie.scheduler.DirectiveIdGenerator; +import gov.nasa.jpl.aerie.scheduler.ProcedureLoader; +import gov.nasa.jpl.aerie.scheduler.model.ActivityType; +import gov.nasa.jpl.aerie.scheduler.model.Plan; +import gov.nasa.jpl.aerie.scheduler.model.PlanningHorizon; +import gov.nasa.jpl.aerie.scheduler.model.SchedulingActivity; +import gov.nasa.jpl.aerie.scheduler.plan.InMemoryEditablePlan; +import gov.nasa.jpl.aerie.scheduler.plan.SchedulerToProcedurePlanAdapter; +import gov.nasa.jpl.aerie.scheduler.simulation.SimulationFacade; +import gov.nasa.jpl.aerie.scheduler.solver.Evaluation; +import org.apache.commons.lang3.NotImplementedException; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import static gov.nasa.jpl.aerie.scheduler.plan.InMemoryEditablePlan.toSchedulingActivityDirective; + +public class Procedure extends Goal { + // private final gov.nasa.jpl.aerie.scheduling.Procedure procedure; + private final Path jarPath; + private final SerializedValue args; + + public Procedure(final PlanningHorizon planningHorizon, Path jarPath, SerializedValue args, boolean simulateAfter) { + this.simulateAfter = simulateAfter; + this.planHorizon = planningHorizon; + this.jarPath = jarPath; + this.args = args; + } + + public void run(Evaluation eval, Plan plan, MissionModel missionModel, Function lookupActivityType, SimulationFacade simulationFacade, DirectiveIdGenerator idGenerator) { + final ProcedureMapper procedureMapper; + try { + procedureMapper = ProcedureLoader.loadProcedure(jarPath); + } catch (ProcedureLoader.ProcedureLoadException e) { + throw new RuntimeException(e); + } + + List newActivities = new ArrayList<>(); + + final var planAdapter = new SchedulerToProcedurePlanAdapter( + plan, + planHorizon + ); + + final var editablePlan = new InMemoryEditablePlan( + missionModel, + idGenerator, + planAdapter, + simulationFacade, + lookupActivityType::apply + ); + + /* + TODO + + Comments from Joel: + - Part of the intent of editablePlan was to be able to re-use it across procedures. + - Could be done by initializing EditablePlanImpl with simulation results + + Duration construction and arithmetic can be less awkward + */ + + procedureMapper.deserialize(this.args).run(editablePlan); + + if (!editablePlan.getUncommittedChanges().isEmpty()) { + throw new NotImplementedException("emit warning"); + } + for (final var edit : editablePlan.getTotalDiff()) { + if (edit instanceof Edit.Create c) { + newActivities.add(toSchedulingActivityDirective(c.getDirective(), lookupActivityType::apply, true)); + } else { + throw new IllegalStateException("Unexpected value: " + edit); + } + } + + final var evaluation = eval.forGoal(this); + for (final var activity : newActivities) { + plan.add(activity); + evaluation.associate(activity, true); + } + evaluation.setScore(0.0); + } +} diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java index de6564cb4a..412ce93ca2 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java @@ -26,6 +26,7 @@ import gov.nasa.jpl.aerie.scheduler.goals.CompositeAndGoal; import gov.nasa.jpl.aerie.scheduler.goals.Goal; import gov.nasa.jpl.aerie.scheduler.goals.OptionGoal; +import gov.nasa.jpl.aerie.scheduler.goals.Procedure; import gov.nasa.jpl.aerie.scheduler.model.Plan; import gov.nasa.jpl.aerie.scheduler.model.PlanInMemory; import gov.nasa.jpl.aerie.scheduler.model.Problem; @@ -320,6 +321,10 @@ private void satisfyGoal(Goal goal) throws SchedulingInterruptedException{ satisfyCompositeGoal(compositeAndGoal); } else if (goal instanceof OptionGoal optionGoal) { satisfyOptionGoal(optionGoal); + } else if (goal instanceof Procedure procedure) { + if (!analysisOnly) { + procedure.run(plan.getEvaluation(), plan, problem.getMissionModel(), this.problem::getActivityType, this.simulationFacade, this.idGenerator); + } } else { satisfyGoalGeneral(goal); } @@ -327,7 +332,6 @@ private void satisfyGoal(Goal goal) throws SchedulingInterruptedException{ this.checkSimBeforeInsertingActivities = checkSimConfig; } - private void satisfyOptionGoal(OptionGoal goal) throws SchedulingInterruptedException{ if (goal.hasOptimizer()) { //try to satisfy all and see what is best diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/Commit.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/Commit.kt new file mode 100644 index 0000000000..f4c512788c --- /dev/null +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/Commit.kt @@ -0,0 +1,7 @@ +package gov.nasa.jpl.aerie.scheduler.plan + +import gov.nasa.ammos.aerie.procedural.scheduling.plan.Edit + +data class Commit( + val diff: List, +) diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt new file mode 100644 index 0000000000..b076c0d009 --- /dev/null +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt @@ -0,0 +1,111 @@ +package gov.nasa.jpl.aerie.scheduler.plan + +import gov.nasa.jpl.aerie.merlin.driver.MissionModel +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue +import gov.nasa.ammos.aerie.procedural.scheduling.plan.Edit +import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan +import gov.nasa.ammos.aerie.procedural.scheduling.plan.NewDirective +import gov.nasa.ammos.aerie.procedural.scheduling.simulation.SimulateOptions +import gov.nasa.jpl.aerie.scheduler.simulation.SimulationFacade +import gov.nasa.ammos.aerie.procedural.timeline.collections.Directives +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart +import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan +import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId +import gov.nasa.jpl.aerie.scheduler.DirectiveIdGenerator +import gov.nasa.jpl.aerie.scheduler.model.* +import java.time.Instant +import kotlin.jvm.optionals.getOrNull +import kotlin.math.absoluteValue +import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults as TimelineSimResults + +data class InMemoryEditablePlan( + private val missionModel: MissionModel<*>, + private var idGenerator: DirectiveIdGenerator, + private val plan: SchedulerToProcedurePlanAdapter, + private val simulationFacade: SimulationFacade, + private val lookupActivityType: (String) -> ActivityType +) : EditablePlan, Plan by plan { + + private val commits = mutableListOf() + var uncommittedChanges = mutableListOf() + private set + + val totalDiff: List + get() = commits.flatMap { it.diff } + + override fun latestResults() = + simulationFacade.latestSimulationData.getOrNull() + ?.let { MerlinToProcedureSimulationResultsAdapter(it.driverResults, false, plan) } + + override fun create(directive: NewDirective): ActivityDirectiveId { + class ParentSearchException(id: ActivityDirectiveId, size: Int): Exception("Expected one parent activity with id $id, found $size") + val id = idGenerator.next() + val parent = when (val s = directive.start) { + is DirectiveStart.Anchor -> { + val parentList = directives() + .filter { it.id == s.parentId } + .collect(totalBounds()) + if (parentList.size != 1) throw ParentSearchException(s.parentId, parentList.size) + parentList.first() + } + is DirectiveStart.Absolute -> null + } + val resolved = directive.resolve(id, parent) + uncommittedChanges.add(Edit.Create(resolved)) + plan.add(resolved.toSchedulingActivityDirective(lookupActivityType, true)) + return id + } + + override fun commit() { + val committedEdits = uncommittedChanges + uncommittedChanges = mutableListOf() + commits.add(Commit(committedEdits)) + } + + override fun rollback(): List { + val result = uncommittedChanges + uncommittedChanges = mutableListOf() + for (edit in result) { + when (edit) { + is Edit.Create -> { + plan.remove(edit.directive.toSchedulingActivityDirective(lookupActivityType, true)) + } + } + } + return result + } + + override fun simulate(options: SimulateOptions): TimelineSimResults { + simulationFacade.simulateWithResults(plan, options.pause.resolve(this)) + return latestResults()!! + } + + // These cannot be implemented with the by keyword, + // because directives() below needs a custom implementation. + override fun totalBounds() = plan.totalBounds() + override fun toRelative(abs: Instant) = plan.toRelative(abs) + override fun toAbsolute(rel: Duration) = plan.toAbsolute(rel) + + companion object { + @JvmStatic fun Directive.toSchedulingActivityDirective(lookupActivityType: (String) -> ActivityType, isNew: Boolean) = SchedulingActivity( + id, + lookupActivityType(type), + when (val s = start) { + is DirectiveStart.Absolute -> s.time + is DirectiveStart.Anchor -> s.offset + }, + Duration.ZERO, + inner.arguments, + null, + when (val s = start) { + is DirectiveStart.Absolute -> null + is DirectiveStart.Anchor -> s.parentId + }, + start is DirectiveStart.Anchor && (start as DirectiveStart.Anchor).anchorPoint == DirectiveStart.Anchor.AnchorPoint.Start, + isNew + ) + } +} diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt new file mode 100644 index 0000000000..ccbc56b0a1 --- /dev/null +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt @@ -0,0 +1,118 @@ +package gov.nasa.jpl.aerie.scheduler.plan + +import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId +import gov.nasa.jpl.aerie.merlin.driver.engine.ProfileSegment +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.collections.Instances +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo +import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceSegmentsOp +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Instance +import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan +import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults +import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId +import java.time.Instant +import kotlin.jvm.optionals.getOrNull + +class MerlinToProcedureSimulationResultsAdapter( + private val results: gov.nasa.jpl.aerie.merlin.driver.SimulationResults, + private val stale: Boolean, + private val plan: Plan +): SimulationResults { + + override fun isStale() = stale + + override fun simBounds(): Interval { + val start = plan.toRelative(results.startTime) + val end = start + results.duration + return start .. end + } + + companion object { + private inline fun convertProfileWithoutGaps(old: List>, converter: (D) -> SerializedValue): List> { + val result: MutableList> = ArrayList(old.size) + var elapsedTime = Duration.ZERO + for (segment in old) { + result.add(Segment( + Interval.betweenClosedOpen(elapsedTime, elapsedTime + segment.extent), + converter(segment.dynamics) + )) + elapsedTime += segment.extent + } + return result + } + } + + override fun > resource(name: String, deserializer: (List>) -> TL): TL { + val profile = + if (results.discreteProfiles.containsKey(name)) convertProfileWithoutGaps(results.discreteProfiles[name]!!.segments) { it } + else if (results.realProfiles.containsKey(name)) convertProfileWithoutGaps(results.realProfiles[name]!!.segments) { + SerializedValue.of(mapOf( + "initial" to SerializedValue.of(it.initial), + "rate" to SerializedValue.of(it.rate) + )) + } + else throw IllegalArgumentException("No such resource $name") + return deserializer.invoke(profile) + } + + private data class FinishedActivityAttributes(val duration: Duration, val computedAttributes: SerializedValue) + private data class CommonActivity( + val arguments: Map, + val type: String, + val directiveId: Long?, + val spanId: Long, + val startTime: Instant, + val finishedActivityAttributes: FinishedActivityAttributes? + ) + + private val commonActivities by lazy { + val result = mutableListOf() + for ((key, a) in results.simulatedActivities) { + result.add(CommonActivity( + a.arguments, + a.type, + a.directiveId.map(ActivityDirectiveId::id).getOrNull(), + key.id, + a.start, + FinishedActivityAttributes(a.duration, a.computedAttributes) + )) + } + for ((key, a) in results.unfinishedActivities) { + result.add(CommonActivity( + a.arguments, + a.type, + a.directiveId.map(ActivityDirectiveId::id).getOrNull(), + key.id, + a.start, + null + )) + } + result + } + + override fun instances(type: String?, deserializer: (SerializedValue) -> A): Instances { + val instances = mutableListOf>() + for (a in commonActivities) { + if (type != null && a.type != type) continue + val startTime = plan.toRelative(a.startTime) + val endTime = a.finishedActivityAttributes?.let { it.duration + startTime } + ?: simBounds().end + val computedAttributes = a.finishedActivityAttributes?.computedAttributes ?: SerializedValue.of(mapOf()) + val serializedActivity = SerializedValue.of(mapOf( + "arguments" to SerializedValue.of(a.arguments), + "computedAttributes" to computedAttributes + )) + instances.add(Instance( + deserializer(serializedActivity), + a.type, + ActivityInstanceId(a.spanId), + a.directiveId?.let { ActivityDirectiveId(it) }, + Interval(startTime, endTime) + )) + } + return Instances(instances) + } +} diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt new file mode 100644 index 0000000000..bca7a2a66b --- /dev/null +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt @@ -0,0 +1,48 @@ +package gov.nasa.jpl.aerie.scheduler.plan + +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue +import gov.nasa.jpl.aerie.scheduler.model.ActivityType +import gov.nasa.jpl.aerie.scheduler.model.PlanningHorizon +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.ammos.aerie.procedural.timeline.collections.Directives +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.minus +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.plus +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart.Anchor.AnchorPoint.Companion.anchorToStart +import java.time.Instant +import gov.nasa.jpl.aerie.scheduler.model.Plan as SchedulerPlan +import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan as TimelinePlan + +data class SchedulerToProcedurePlanAdapter( + private val schedulerPlan: SchedulerPlan, + private val planningHorizon: PlanningHorizon, +): TimelinePlan, SchedulerPlan by schedulerPlan { + override fun totalBounds() = Interval.between(Duration.ZERO, planningHorizon.aerieHorizonDuration) + + override fun toRelative(abs: Instant) = abs - planningHorizon.startInstant + + override fun toAbsolute(rel: Duration) = planningHorizon.startInstant + rel + + override fun directives(type: String?, deserializer: (SerializedValue) -> A): Directives { + val schedulerActivities = (if (type == null) schedulerPlan.activities else schedulerPlan.activitiesByType[ActivityType(type)]) + ?: throw Exception("could not find activities by type $type") + + val result = ArrayList>(schedulerActivities.size) + for (activity in schedulerActivities) { + result.add( + Directive( + deserializer(SerializedValue.of(activity.arguments)), + "Name unavailable", + activity.id, + activity.type.name, + if (activity.anchorId == null) DirectiveStart.Absolute(activity.startOffset) + else DirectiveStart.Anchor(activity.anchorId, activity.startOffset, anchorToStart(activity.anchoredToStart)) + ) + ) + } + return Directives(result) + } + +} diff --git a/scheduler-server/build.gradle b/scheduler-server/build.gradle index c4ddee4905..88cc2434bf 100644 --- a/scheduler-server/build.gradle +++ b/scheduler-server/build.gradle @@ -24,6 +24,7 @@ dependencies { implementation project(':permissions') implementation project(':constraints') implementation project(':scheduler-driver') + implementation project(':procedural:scheduling') implementation project(':type-utils') implementation 'io.javalin:javalin:5.6.3' diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalRecord.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalRecord.java index 620745011e..94bbbf1828 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalRecord.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalRecord.java @@ -1,7 +1,10 @@ package gov.nasa.jpl.aerie.scheduler.server.models; +import java.nio.file.Path; +import java.util.Optional; + public record GoalRecord( GoalId id, String name, - GoalSource definition, + GoalType type, boolean simulateAfter) {} diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java new file mode 100644 index 0000000000..7ca3cc6fec --- /dev/null +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java @@ -0,0 +1,9 @@ +package gov.nasa.jpl.aerie.scheduler.server.models; + +import javax.json.JsonObject; +import java.nio.file.Path; + +public sealed interface GoalType { + record EDSL(String source) implements GoalType {} + record JAR(Path path, String args) implements GoalType {} +} diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/SchedulingDSL.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/SchedulingDSL.java index fb1915a172..45a65d30b8 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/SchedulingDSL.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/SchedulingDSL.java @@ -9,6 +9,7 @@ import gov.nasa.jpl.aerie.json.SumParsers; import gov.nasa.jpl.aerie.json.Unit; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; import gov.nasa.jpl.aerie.scheduler.TimeUtility; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeAnchor; import gov.nasa.jpl.aerie.scheduler.model.PersistentTimeAnchor; @@ -16,7 +17,9 @@ import gov.nasa.jpl.aerie.scheduler.server.services.MerlinDatabaseService; import org.apache.commons.lang3.tuple.Pair; +import java.nio.file.Path; import java.util.List; +import java.util.Map; import java.util.Optional; import static gov.nasa.jpl.aerie.constraints.json.ConstraintParsers.profileExpressionP; @@ -297,6 +300,10 @@ record GoalApplyWhen( GoalSpecifier goal, Expression windows ) implements GoalSpecifier {} + record Procedure( + Path jarPath, + SerializedValue arguments + ) implements GoalSpecifier {} } public record LinearResource(String name) {} diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java index cd98ccb6d6..00e61a6cf7 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java @@ -3,17 +3,20 @@ import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; import gov.nasa.jpl.aerie.scheduler.server.models.GoalSource; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import org.intellij.lang.annotations.Language; +import java.nio.file.Path; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; /*package-local*/ final class GetSpecificationGoalsAction implements AutoCloseable { private final @Language("SQL") String sql = """ - select s.goal_id, gd.revision, gm.name, gd.definition, s.goal_invocation_id, s.simulate_after + select s.goal_id, gd.revision, gm.name, gd.definition, s.goal_invocation_id, s.simulate_after, gd.type, encode(f.path, 'escape') as path, s.arguments from scheduler.scheduling_specification_goals s left join scheduler.scheduling_goal_definition gd using (goal_id) left join scheduler.scheduling_goal_metadata gm on s.goal_id = gm.id @@ -46,7 +49,15 @@ public List get(final long specificationId) throws SQLException { final var name = resultSet.getString("name"); final var definition = resultSet.getString("definition"); final var simulateAfter = resultSet.getBoolean("simulate_after"); - goals.add(new GoalRecord(new GoalId(id, revision, goalInvocationId), name, new GoalSource(definition), simulateAfter)); + final var type = resultSet.getString("type"); + final var path = resultSet.getString("path"); + final var args = resultSet.getString("arguments"); + goals.add(new GoalRecord( + new GoalId(id, revision, Optional.of(goalInvocationId)), + name, + type.equals("JAR") ? new GoalType.JAR(Path.of(path), args) : new GoalType.EDSL(definition), + simulateAfter + )); } return goals; diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GoalBuilder.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GoalBuilder.java index 008208cfdc..5a9b6b6569 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GoalBuilder.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GoalBuilder.java @@ -22,6 +22,7 @@ import gov.nasa.jpl.aerie.scheduler.goals.CompositeAndGoal; import gov.nasa.jpl.aerie.scheduler.goals.Goal; import gov.nasa.jpl.aerie.scheduler.goals.OptionGoal; +import gov.nasa.jpl.aerie.scheduler.goals.Procedure; import gov.nasa.jpl.aerie.scheduler.goals.RecurrenceGoal; import gov.nasa.jpl.aerie.scheduler.model.ActivityType; import gov.nasa.jpl.aerie.scheduler.model.PersistentTimeAnchor; @@ -162,6 +163,10 @@ public static Goal goalOfGoalSpecifier( } return builder.build(); } + + case SchedulingDSL.GoalSpecifier.Procedure g -> { + return new Procedure(planningHorizon, g.jarPath(), g.arguments(), simulateAfter); + } } } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java index f1d3e5e111..e0801bb649 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java @@ -61,13 +61,7 @@ import java.time.Instant; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.TreeMap; +import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -463,21 +457,30 @@ public Map updatePlanActivityDirective return ids; } -@Override + @Override public void updatePlanActivityDirectiveAnchors(final PlanId planId, final Plan plan, final Map uploadIdMap) throws MerlinServiceException, IOException { - for (final SchedulingActivity act: plan.getActivities()) { + final var request = new StringBuilder(); + final var acts = plan.getActivities(); + request.append("mutation {"); + var hasUpdate = false; + for (final SchedulingActivity act: acts) { if (act.isNew() && act.anchorId() != null) { - final var request = """ - mutation { - update_activity_directive_by_pk(pk_columns: {id: %d, plan_id: %d}, _set: {anchor_id: %d}) { - id - } - }""".formatted(uploadIdMap.get(act.id()).id(), planId.id(), uploadIdMap.get(act.anchorId()).id()); - final var response = postRequest(request); + hasUpdate = true; + final var id = uploadIdMap.get(act.id()).id(); + request.append(""" + update_%d: update_activity_directive_by_pk(pk_columns: {id: %d, plan_id: %d}, _set: {anchor_id: %d}) { + id + } + """.formatted(id, id, planId.id(), uploadIdMap.get(act.anchorId()).id()) + ); } } + if (hasUpdate) { + request.append("}"); + postRequest(request.toString()); + } } /** @@ -577,7 +580,7 @@ mutation createAllPlanActivityDirectives($activities: [activity_directive_insert .add("plan_id", planId.id()) .add("type", act.getType().getName()) .add("start_offset", act.startOffset().toString()) - .add("anchored_to_start", act.anchoredToStart()); + .add("anchored_to_start", act.anchorId() == null || act.anchoredToStart()); //add duration to parameters if controllable final var insertionObjectArguments = Json.createObjectBuilder(); diff --git a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java index 57fbdad741..e73b4635b0 100644 --- a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java +++ b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java @@ -3,6 +3,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; @@ -20,10 +21,13 @@ import java.util.jar.JarFile; import java.util.stream.Collectors; +import gov.nasa.jpl.aerie.json.JsonParser; +import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId; import gov.nasa.jpl.aerie.merlin.driver.MissionModel; import gov.nasa.jpl.aerie.merlin.driver.MissionModelLoader; import gov.nasa.jpl.aerie.merlin.driver.SimulationEngineConfiguration; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; +import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser; import gov.nasa.jpl.aerie.merlin.protocol.model.SchedulerModel; import gov.nasa.jpl.aerie.merlin.protocol.model.SchedulerPlugin; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; @@ -44,12 +48,14 @@ import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSpecificationException; import gov.nasa.jpl.aerie.scheduler.server.exceptions.ResultsProtocolFailure; import gov.nasa.jpl.aerie.scheduler.server.exceptions.SpecificationLoadException; +import gov.nasa.jpl.aerie.scheduler.server.http.InvalidEntityException; import gov.nasa.jpl.aerie.scheduler.server.http.InvalidJsonException; import gov.nasa.jpl.aerie.scheduler.server.http.ResponseSerializers; import gov.nasa.jpl.aerie.scheduler.server.models.DatasetId; import gov.nasa.jpl.aerie.scheduler.server.models.ExternalProfiles; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import gov.nasa.jpl.aerie.scheduler.server.models.MerlinPlan; import gov.nasa.jpl.aerie.scheduler.server.models.PlanId; import gov.nasa.jpl.aerie.scheduler.server.models.PlanMetadata; @@ -74,6 +80,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.json.Json; +import javax.json.stream.JsonParsingException; + /** * agent that handles posed scheduling requests by blocking the requester thread until scheduling is complete * @@ -188,20 +197,32 @@ public void schedule( final var compiledGoals = new ArrayList>(); final var failedGoals = new ArrayList>>(); for (final var goalRecord : specification.goalsByPriority()) { - final var result = compileGoalDefinition( - merlinDatabaseService, - planMetadata.planId(), - goalRecord.definition().source(), - schedulingDSLCompilationService, - externalProfiles.resourceTypes()); - if (result instanceof SchedulingDSLCompilationService.SchedulingDSLCompilationResult.Success r) { - compiledGoals.add(Pair.of(goalRecord, r.value())); - } else if (result instanceof SchedulingDSLCompilationService.SchedulingDSLCompilationResult.Error r) { - failedGoals.add(Pair.of(goalRecord.id(), r.errors())); - } else { - throw new Error("Unhandled variant of %s: %s".formatted( - SchedulingDSLCompilationService.SchedulingDSLCompilationResult.class.getSimpleName(), - result)); + switch (goalRecord.type()) { + case GoalType.EDSL edsl -> { + final var result = compileGoalDefinition( + merlinDatabaseService, + planMetadata.planId(), + edsl.source(), + schedulingDSLCompilationService, + externalProfiles.resourceTypes()); + if (result instanceof SchedulingDSLCompilationService.SchedulingDSLCompilationResult.Success r) { + compiledGoals.add(Pair.of(goalRecord, r.value())); + } else if (result instanceof SchedulingDSLCompilationService.SchedulingDSLCompilationResult.Error r) { + failedGoals.add(Pair.of(goalRecord.id(), r.errors())); + } else { + throw new Error("Unhandled variant of %s: %s".formatted( + SchedulingDSLCompilationService.SchedulingDSLCompilationResult.class.getSimpleName(), + result)); + } + } + case GoalType.JAR jar -> { + try { + final var serializedValue = parseJson(jar.args(), new SerializedValueJsonParser()); + compiledGoals.add(Pair.of(goalRecord, new SchedulingDSL.GoalSpecifier.Procedure(modelJarsDir.resolve(jar.path()), serializedValue))); + } catch (InvalidJsonException | InvalidEntityException e) { + throw new RuntimeException(e); + } + } } } if (!failedGoals.isEmpty()) { @@ -641,4 +662,15 @@ private ScheduleResults collectResults(final Plan plan, Map T parseJson(final String jsonStr, final JsonParser parser) + throws InvalidJsonException, InvalidEntityException + { + try (final var reader = Json.createReader(new StringReader(jsonStr))) { + final var requestJson = reader.readValue(); + final var result = parser.parse(requestJson); + return result.getSuccessOrThrow(reason -> new InvalidEntityException(List.of(reason))); + } catch (JsonParsingException e) { + throw new InvalidJsonException(e); + } + } } diff --git a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java index bd5c774f8d..eb5dd506b2 100644 --- a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java +++ b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java @@ -38,6 +38,7 @@ import gov.nasa.jpl.aerie.scheduler.server.config.PlanOutputMode; import gov.nasa.jpl.aerie.scheduler.server.http.SchedulerParsers; import gov.nasa.jpl.aerie.scheduler.server.models.ExternalProfiles; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import gov.nasa.jpl.aerie.scheduler.server.models.SchedulingConditionId; import gov.nasa.jpl.aerie.scheduler.server.models.SchedulingConditionRecord; import gov.nasa.jpl.aerie.scheduler.server.models.SchedulingConditionSource; @@ -2197,7 +2198,7 @@ private SchedulingRunResults runScheduler( final var goalsByPriority = new ArrayList(); for (final var goal : goals) { - goalsByPriority.add(new GoalRecord(goal.goalId(), "test goal", new GoalSource(goal.definition()), goal.simulateAfter())); + goalsByPriority.add(new GoalRecord(goal.goalId(), "test goal", new GoalType.EDSL(goal.definition()), goal.simulateAfter())); } final var specificationService = new SpecificationService(new MockSpecificationRepository(Map.of(new SpecificationId(1L), new Specification( new SpecificationId(1L), From b0aaadbf552d4d2020c573e560fbbb287b030af1 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Tue, 20 Aug 2024 11:37:13 -0700 Subject: [PATCH 023/108] Implement database changes for scheduling procedures --- .github/scripts/compareDatabasesDown.sh | 1 + .github/scripts/explanations | 2 + .github/workflows/pgcmp.yml | 2 + .../scheduler/scheduling_goal_definition.yaml | 7 ++- .../scheduling_specification_goals.yaml | 8 +-- .../Aerie/10_procedural_scheduling/down.sql | 35 +++++++++++++ .../Aerie/10_procedural_scheduling/up.sql | 50 +++++++++++++++++++ .../sql/applied_migrations.sql | 1 + .../postgres-init-db/sql/init_scheduler.sql | 3 ++ .../scheduler/scheduling_goal_definition.sql | 29 +++++++++-- .../scheduling_goal_analysis.sql | 4 ++ .../scheduling_specification_goals.sql | 5 ++ .../sql/types/scheduler/goal_type.sql | 1 + 13 files changed, 139 insertions(+), 9 deletions(-) create mode 100644 .github/scripts/explanations create mode 100644 deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql create mode 100644 deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql create mode 100644 deployment/postgres-init-db/sql/types/scheduler/goal_type.sql diff --git a/.github/scripts/compareDatabasesDown.sh b/.github/scripts/compareDatabasesDown.sh index be003b16f6..711a3f7fd2 100755 --- a/.github/scripts/compareDatabasesDown.sh +++ b/.github/scripts/compareDatabasesDown.sh @@ -13,6 +13,7 @@ PGCBADEXPLAIN=./comparison/badexplanations.txt \ PGDB=postgres \ PGBINDIR=/usr/bin \ PGCOMITSCHEMAS="('hdb_catalog'),('pg_catalog'),('information_schema')" \ +PGCEXPLANATIONS=./explanations \ ./pgcmp return_code=$? diff --git a/.github/scripts/explanations b/.github/scripts/explanations new file mode 100644 index 0000000000..5676b2cc8c --- /dev/null +++ b/.github/scripts/explanations @@ -0,0 +1,2 @@ +trigger scheduler scheduler.scheduling_goal_definition."notify_hasura_refreshSchedulingProcedureParameterTypes_INSERT"/"INSERT" missing in 1st DB From_RefreshSchedProcParm_Action 2 +trigger scheduler scheduler.scheduling_goal_definition."notify_hasura_refreshSchedulingProcedureParameterTypes_UPDATE"/"UPDATE" missing in 1st DB From_RefreshSchedProcParm_Action 2 diff --git a/.github/workflows/pgcmp.yml b/.github/workflows/pgcmp.yml index a5b4bfb7a5..25aebae79b 100644 --- a/.github/workflows/pgcmp.yml +++ b/.github/workflows/pgcmp.yml @@ -271,6 +271,7 @@ jobs: - name: Compare Databases id: dbcmp run: | + cp ./.github/scripts/explanations pgcmp cp ./.github/scripts/compareDatabasesUp.sh pgcmp/compareDatabases.sh cd pgcmp ./compareDatabases.sh @@ -332,6 +333,7 @@ jobs: - name: Compare Databases id: dbcmp run: | + cp ./.github/scripts/explanations pgcmp cp ./.github/scripts/compareDatabasesDown.sh pgcmp/compareDatabases.sh cd pgcmp ./compareDatabases.sh diff --git a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml index 5d1185a607..a6de3d2a7b 100644 --- a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml +++ b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml @@ -7,6 +7,9 @@ object_relationships: - name: metadata using: foreign_key_constraint_on: goal_id + - name: uploaded_jar + using: + foreign_key_constraint_on: uploaded_jar_id array_relationships: - name: analyses using: @@ -64,13 +67,13 @@ select_permissions: insert_permissions: - role: aerie_admin permission: - columns: [goal_id, definition] + columns: [goal_id, definition, type, uploaded_jar_id, parameter_schema] check: {} set: author: "x-hasura-user-id" - role: user permission: - columns: [goal_id, definition] + columns: [goal_id, definition, type, uploaded_jar_id, parameter_schema] check: {"_or":[{"metadata":{"public":{"_eq":true}}},{"metadata":{"owner":{"_eq":"X-Hasura-User-Id"}}}]} set: author: "x-hasura-user-id" diff --git a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_specification/scheduling_specification_goals.yaml b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_specification/scheduling_specification_goals.yaml index b3d5254496..a18ae58fd4 100644 --- a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_specification/scheduling_specification_goals.yaml +++ b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_specification/scheduling_specification_goals.yaml @@ -35,20 +35,20 @@ select_permissions: insert_permissions: - role: aerie_admin permission: - columns: [specification_id, goal_id, goal_revision, priority, enabled, simulate_after, goal_invocation_id] + columns: [specification_id, goal_id, goal_revision, priority, enabled, simulate_after, arguments, goal_invocation_id] check: {} - role: user permission: - columns: [specification_id, goal_id, goal_revision, priority, enabled, simulate_after, goal_invocation_id] + columns: [specification_id, goal_id, goal_revision, priority, enabled, simulate_after, arguments, goal_invocation_id] check: {} update_permissions: - role: aerie_admin permission: - columns: [goal_revision, priority, enabled, simulate_after] + columns: [goal_revision, priority, enabled, simulate_after, arguments] filter: {} - role: user permission: - columns: [goal_revision, priority, enabled, simulate_after] + columns: [goal_revision, priority, enabled, simulate_after, arguments] filter: {} delete_permissions: - role: aerie_admin diff --git a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql new file mode 100644 index 0000000000..6d913616ae --- /dev/null +++ b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql @@ -0,0 +1,35 @@ +-- delete all scheduling procedures from specs +-- (on delete is restricted) +delete from scheduler.scheduling_specification_goals sg + using scheduler.scheduling_goal_definition gd + where gd.goal_id = sg.goal_id + and gd.type = 'JAR'::scheduler.goal_type; + +-- delete all scheduling procedure definitions +delete from scheduler.scheduling_goal_metadata gm + using scheduler.scheduling_goal_definition gd + where gm.id = gd.goal_id + and gd.type = 'JAR'::scheduler.goal_type; + +alter table scheduler.scheduling_goal_analysis + drop column arguments; + +alter table scheduler.scheduling_specification_goals + drop column arguments; + +alter table scheduler.scheduling_goal_definition + drop constraint check_goal_definition_type_consistency, + drop constraint scheduling_procedure_has_uploaded_jar, + + drop column type, + drop column uploaded_jar_id, + drop column parameter_schema, + + alter column definition set not null; + +comment on column scheduler.scheduling_goal_definition.definition is e'' + 'An executable expression in the Merlin scheduling language.'; + +drop type scheduler.goal_type; + +call migrations.mark_migration_rolled_back('10'); diff --git a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql new file mode 100644 index 0000000000..d0b7a00730 --- /dev/null +++ b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql @@ -0,0 +1,50 @@ +create type scheduler.goal_type as enum ('EDSL', 'JAR'); + +alter table scheduler.scheduling_goal_definition + add column type scheduler.goal_type not null default 'EDSL', + add column uploaded_jar_id integer, + add column parameter_schema jsonb, + + alter column definition drop not null, + + add constraint scheduling_procedure_has_uploaded_jar + foreign key (uploaded_jar_id) + references merlin.uploaded_file + on update cascade + on delete restrict, + + add constraint check_goal_definition_type_consistency + check ( + (type = 'EDSL' and definition is not null and uploaded_jar_id is null) + or + (type = 'JAR' and uploaded_jar_id is not null and definition is null) + ); + +comment on column scheduler.scheduling_goal_definition.type is e'' + 'The type of this goal definition, "EDSL" or "JAR".'; +comment on column scheduler.scheduling_goal_definition.definition is e'' + 'An executable expression in the Merlin scheduling language.' + 'Should be non-null when type is EDSL'; +comment on column scheduler.scheduling_goal_definition.uploaded_jar_id is e'' + 'The foreign key to the uploaded_file entry containing the procedure jar' + 'Should be non-null when type is JAR'; +comment on column scheduler.scheduling_goal_definition.parameter_schema is e'' + 'The schema for parameters that can be passed to goal instances using this definition.' + 'Similar schema to parameter_set''s in Merlin.'; + +alter table scheduler.scheduling_specification_goals + add column arguments jsonb not null default '{}'::jsonb; + +comment on column scheduler.scheduling_specification_goals.arguments is e'' + 'The arguments that will be passed to this goal when invoked.' + 'Follows scheduler.scheduling_goal_definition.parameter_schema.' + 'Only valid for procedural goals.'; + +alter table scheduler.scheduling_goal_analysis + add column arguments jsonb not null default '{}'::jsonb; + +comment on column scheduler.scheduling_goal_analysis.arguments is e'' + 'The "as run" arguments passed to this goal during the scheduling run.' + 'Follows scheduler.scheduling_goal_definition.parameter_schema.'; + +call migrations.mark_migration_applied('10'); diff --git a/deployment/postgres-init-db/sql/applied_migrations.sql b/deployment/postgres-init-db/sql/applied_migrations.sql index 4f0f55d200..245dc51deb 100644 --- a/deployment/postgres-init-db/sql/applied_migrations.sql +++ b/deployment/postgres-init-db/sql/applied_migrations.sql @@ -12,3 +12,4 @@ call migrations.mark_migration_applied('6'); call migrations.mark_migration_applied('7'); call migrations.mark_migration_applied('8'); call migrations.mark_migration_applied('9'); +call migrations.mark_migration_applied('10'); diff --git a/deployment/postgres-init-db/sql/init_scheduler.sql b/deployment/postgres-init-db/sql/init_scheduler.sql index a28d050b0a..870d5d7504 100644 --- a/deployment/postgres-init-db/sql/init_scheduler.sql +++ b/deployment/postgres-init-db/sql/init_scheduler.sql @@ -6,6 +6,9 @@ - Views must be loaded after all their dependent tables and functions */ begin; + -- Types + \ir types/scheduler/goal_type.sql + -- Tables -- Scheduling Goals \ir tables/scheduler/scheduling_goal_metadata.sql diff --git a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_goal_definition.sql b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_goal_definition.sql index 61dc4efdf2..1339e0193c 100644 --- a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_goal_definition.sql +++ b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_goal_definition.sql @@ -2,7 +2,10 @@ create table scheduler.scheduling_goal_definition( goal_id integer not null, revision integer not null default 0, - definition text not null, + type scheduler.goal_type not null default 'EDSL', + definition text, + uploaded_jar_id integer, + parameter_schema jsonb, author text, created_at timestamptz not null default now(), @@ -13,19 +16,39 @@ create table scheduler.scheduling_goal_definition( references scheduler.scheduling_goal_metadata on update cascade on delete cascade, + constraint scheduling_procedure_has_uploaded_jar + foreign key (uploaded_jar_id) + references merlin.uploaded_file + on update cascade + on delete restrict, constraint goal_definition_author_exists foreign key (author) references permissions.users on update cascade - on delete set null + on delete set null, + constraint check_goal_definition_type_consistency + check ( + (type = 'EDSL' and definition is not null and uploaded_jar_id is null) + or + (type = 'JAR' and uploaded_jar_id is not null and definition is null) + ) ); comment on table scheduler.scheduling_goal_definition is e'' 'The specific revisions of a scheduling goal''s definition'; comment on column scheduler.scheduling_goal_definition.revision is e'' 'An identifier of this definition.'; +comment on column scheduler.scheduling_goal_definition.type is e'' + 'The type of this goal definition, "EDSL" or "JAR".'; comment on column scheduler.scheduling_goal_definition.definition is e'' - 'An executable expression in the Merlin scheduling language.'; + 'An executable expression in the Merlin scheduling language.' + 'Should be non-null when type is EDSL'; +comment on column scheduler.scheduling_goal_definition.uploaded_jar_id is e'' + 'The foreign key to the uploaded_file entry containing the procedure jar' + 'Should be non-null when type is JAR'; +comment on column scheduler.scheduling_goal_definition.parameter_schema is e'' + 'The schema for parameters that can be passed to goal instances using this definition.' + 'Similar schema to parameter_set''s in Merlin.'; comment on column scheduler.scheduling_goal_definition.author is e'' 'The user who authored this revision.'; comment on column scheduler.scheduling_goal_definition.created_at is e'' diff --git a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis.sql b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis.sql index 58cfaa1ef0..e678f2278a 100644 --- a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis.sql +++ b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_run/scheduling_goal_analysis.sql @@ -4,6 +4,7 @@ create table scheduler.scheduling_goal_analysis ( goal_invocation_id integer not null, goal_revision integer not null, satisfied boolean not null, + arguments jsonb not null default '{}'::jsonb, constraint scheduling_goal_analysis_primary_key primary key (analysis_id, goal_invocation_id), @@ -31,3 +32,6 @@ comment on column scheduler.scheduling_goal_analysis.goal_revision is e'' 'The associated version of the goal definition used.'; comment on column scheduler.scheduling_goal_analysis.satisfied is e'' 'Whether the associated goal was satisfied by the scheduling run.'; +comment on column scheduler.scheduling_goal_analysis.arguments is e'' + 'The "as run" arguments passed to this goal during the scheduling run.' + 'Follows scheduler.scheduling_goal_definition.parameter_schema.'; diff --git a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_specification/scheduling_specification_goals.sql b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_specification/scheduling_specification_goals.sql index d945ed51f7..a227daf1fb 100644 --- a/deployment/postgres-init-db/sql/tables/scheduler/scheduling_specification/scheduling_specification_goals.sql +++ b/deployment/postgres-init-db/sql/tables/scheduler/scheduling_specification/scheduling_specification_goals.sql @@ -5,6 +5,7 @@ create table scheduler.scheduling_specification_goals ( goal_revision integer, -- latest is null priority integer not null, enabled boolean not null default true, + arguments jsonb not null default '{}'::jsonb, simulate_after boolean not null default true, @@ -46,6 +47,10 @@ comment on column scheduler.scheduling_specification_goals.priority is e'' 'scheduling goals within the same specification.'; comment on column scheduler.scheduling_specification_goals.enabled is e'' 'Whether to run a given goal. Defaults to TRUE.'; +comment on column scheduler.scheduling_specification_goals.arguments is e'' + 'The arguments that will be passed to this goal when invoked.' + 'Follows scheduler.scheduling_goal_definition.parameter_schema.' + 'Only valid for procedural goals.'; comment on column scheduler.scheduling_specification_goals.simulate_after is e'' 'Whether to re-simulate after evaluating this goal and before the next goal.'; diff --git a/deployment/postgres-init-db/sql/types/scheduler/goal_type.sql b/deployment/postgres-init-db/sql/types/scheduler/goal_type.sql new file mode 100644 index 0000000000..6942396a05 --- /dev/null +++ b/deployment/postgres-init-db/sql/types/scheduler/goal_type.sql @@ -0,0 +1 @@ +create type scheduler.goal_type as enum ('EDSL', 'JAR'); From 8f17c64ad1d3e41fed797ce84c96b79c7ca5eadd Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Wed, 7 Aug 2024 06:27:38 -0700 Subject: [PATCH 024/108] Add hook to update scheduling procedure parameters --- .../scheduler/scheduling_goal_definition.yaml | 17 ++++++ .../scheduler/server/SchedulerAppDriver.java | 1 + .../NoSuchSchedulingGoalException.java | 12 ++++ .../server/http/SchedulerBindings.java | 26 +++++++++ .../server/http/SchedulerParsers.java | 15 +++++ .../scheduler/server/models/HasuraAction.java | 1 + .../remotes/SpecificationRepository.java | 6 ++ .../postgres/GetSchedulingGoalAction.java | 56 +++++++++++++++++++ .../PostgresSpecificationRepository.java | 27 +++++++++ ...teSchedulingGoalParameterSchemaAction.java | 38 +++++++++++++ .../server/services/SpecificationService.java | 36 ++++++++++++ .../services/MockSpecificationRepository.java | 14 +++++ 12 files changed, 249 insertions(+) create mode 100644 scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/exceptions/NoSuchSchedulingGoalException.java create mode 100644 scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java create mode 100644 scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java diff --git a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml index a6de3d2a7b..d7392d6f75 100644 --- a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml +++ b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml @@ -92,3 +92,20 @@ delete_permissions: {"_or":[ {"author": {"_eq": "X-Hasura-User-Id"}}, {"metadata":{"owner":{"_eq":"X-Hasura-User-Id"}}}]} + +event_triggers: + - definition: + enable_manual: false + insert: + columns: "*" + update: + columns: + - goal_id + - revision + - uploaded_jar_id + name: refreshSchedulingProcedureParameterTypes + retry_conf: + interval_sec: 10 + num_retries: 0 + timeout_sec: 300 + webhook: "{{AERIE_SCHEDULER_URL}}/refreshSchedulingProcedureParameterTypes" diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/SchedulerAppDriver.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/SchedulerAppDriver.java index 5be5baddc5..d5f3ea0ea0 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/SchedulerAppDriver.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/SchedulerAppDriver.java @@ -62,6 +62,7 @@ public static void main(final String[] args) { //establish bindings to the service layers final var bindings = new SchedulerBindings( + specificationService, schedulerService, scheduleAction, generateSchedulingLibAction, diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/exceptions/NoSuchSchedulingGoalException.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/exceptions/NoSuchSchedulingGoalException.java new file mode 100644 index 0000000000..421ae3705e --- /dev/null +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/exceptions/NoSuchSchedulingGoalException.java @@ -0,0 +1,12 @@ +package gov.nasa.jpl.aerie.scheduler.server.exceptions; + +import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; + +public final class NoSuchSchedulingGoalException extends Exception { + public final GoalId goalId; + + public NoSuchSchedulingGoalException(final GoalId goalId) { + super("No scheduling goal exists with id `" + goalId.id() + "` and revision `" + goalId.revision() + "`"); + this.goalId = goalId; + } +} diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerBindings.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerBindings.java index bfb4b44280..eba7905773 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerBindings.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerBindings.java @@ -8,6 +8,7 @@ import java.util.Objects; import static gov.nasa.jpl.aerie.scheduler.server.http.ResponseSerializers.*; import static gov.nasa.jpl.aerie.scheduler.server.http.SchedulerParsers.hasuraSchedulingDSLTypescriptActionP; +import static gov.nasa.jpl.aerie.scheduler.server.http.SchedulerParsers.hasuraSchedulingGoalEventTriggerP; import static gov.nasa.jpl.aerie.scheduler.server.http.SchedulerParsers.hasuraSpecificationActionP; import static io.javalin.apibuilder.ApiBuilder.*; import gov.nasa.jpl.aerie.json.JsonParser; @@ -23,6 +24,7 @@ import gov.nasa.jpl.aerie.scheduler.server.services.GenerateSchedulingLibAction; import gov.nasa.jpl.aerie.scheduler.server.services.ScheduleAction; import gov.nasa.jpl.aerie.scheduler.server.services.SchedulerService; +import gov.nasa.jpl.aerie.scheduler.server.services.SpecificationService; import io.javalin.Javalin; import io.javalin.http.Context; import io.javalin.plugin.Plugin; @@ -37,12 +39,14 @@ * @param permissionsService service that authorizes action requests */ public record SchedulerBindings( + SpecificationService specificationService, SchedulerService schedulerService, ScheduleAction scheduleAction, GenerateSchedulingLibAction generateSchedulingLibAction, PermissionsService permissionsService ) implements Plugin { public SchedulerBindings { + Objects.requireNonNull(specificationService); Objects.requireNonNull(schedulerService); Objects.requireNonNull(scheduleAction); Objects.requireNonNull(generateSchedulingLibAction); @@ -64,6 +68,7 @@ public void apply(final Javalin javalin) { path("schedule", () -> post(this::schedule)); path("health", () -> get(ctx -> ctx.status(200))); path("schedulingDslTypescript", () -> post(this::getSchedulingDslTypescript)); + path("refreshSchedulingProcedureParameterTypes", () -> post(this::refreshSchedulingProcedureParameterTypes)); }); } @@ -152,6 +157,27 @@ private void getSchedulingDslTypescript(final Context ctx) { } } + /** + * action bound to the /refreshSchedulingProcedureParameterTypes endpoint + * + * Responsible for loading an uploaded procedure jar, asking for its parameter value schema and saving that to the database + * + * @param ctx the http context of the request from which to read input or post results + */ + private void refreshSchedulingProcedureParameterTypes(final Context ctx) { + try { + final var body = parseJson(ctx.body(), hasuraSchedulingGoalEventTriggerP); + final var goalId = body.goalId(); + final var revision = body.revision(); + this.specificationService.refreshSchedulingProcedureParameterTypes(goalId, revision); + ctx.status(200); + } catch (final InvalidEntityException ex) { + ctx.status(400).result(serializeInvalidEntityException(ex).toString()); + } catch (final InvalidJsonException ex) { + ctx.status(400).result(serializeInvalidJsonException(ex).toString()); + } + } + /** * parses the provided json string into the object type understood by the given parser * diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerParsers.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerParsers.java index abf6b5da35..260a846a41 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerParsers.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerParsers.java @@ -106,4 +106,19 @@ private static JsonParser> hasura .map( untuple((missionModelId, planId) -> new HasuraAction.MissionModelIdInput(missionModelId, planId.flatMap($->$))), input -> tuple(input.missionModelId(), Optional.of(input.planId())))); + + public static final JsonParser hasuraSchedulingGoalEventTriggerP + = productP + .field("event", productP + .field("data", productP + .field("new", productP + .field("goal_id", longP) + .field("revision", longP) + .rest()) + .rest()) + .rest()) + .rest() + .map( + untuple(HasuraAction.HasuraSchedulingGoalEvent::new), + $ -> tuple($.goalId(), $.revision())); } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/HasuraAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/HasuraAction.java index c3c0c9b88a..49bbb33d1a 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/HasuraAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/HasuraAction.java @@ -12,4 +12,5 @@ public sealed interface Input permits SpecificationInput, MissionModelIdInput { public record SpecificationInput(SpecificationId specificationId) implements Input { } public record MissionModelIdInput(MissionModelId missionModelId, Optional planId) implements Input { } + public record HasuraSchedulingGoalEvent(long goalId, long revision) { } } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/SpecificationRepository.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/SpecificationRepository.java index fbd62ab67b..1cb1436f77 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/SpecificationRepository.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/SpecificationRepository.java @@ -1,7 +1,11 @@ package gov.nasa.jpl.aerie.scheduler.server.remotes; +import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; +import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSchedulingGoalException; import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSpecificationException; import gov.nasa.jpl.aerie.scheduler.server.exceptions.SpecificationLoadException; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; import gov.nasa.jpl.aerie.scheduler.server.models.Specification; import gov.nasa.jpl.aerie.scheduler.server.models.SpecificationId; import gov.nasa.jpl.aerie.scheduler.server.remotes.postgres.SpecificationRevisionData; @@ -11,4 +15,6 @@ public interface SpecificationRepository { Specification getSpecification(SpecificationId specificationId) throws NoSuchSpecificationException, SpecificationLoadException; SpecificationRevisionData getSpecificationRevisionData(SpecificationId specificationId) throws NoSuchSpecificationException; + GoalRecord getGoal(GoalId goalId) throws NoSuchSchedulingGoalException; + void updateGoalParameterSchema(GoalId goalId, ValueSchema schema); } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java new file mode 100644 index 0000000000..0a32afd1a3 --- /dev/null +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java @@ -0,0 +1,56 @@ +package gov.nasa.jpl.aerie.scheduler.server.remotes.postgres; + +import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalSource; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; +import org.intellij.lang.annotations.Language; + +import java.nio.file.Path; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/*package-local*/ final class GetSchedulingGoalAction implements AutoCloseable { + private final @Language("SQL") String sql = """ + select gd.goal_id, gd.revision, gm.name, gd.definition, gd.type, encode(f.path, 'escape') as path + from scheduler.scheduling_goal_definition gd + left join scheduler.scheduling_goal_metadata gm on gd.goal_id = gm.id + left join merlin.uploaded_file f on gd.uploaded_jar_id = f.id + where gd.goal_id = ? and gd.revision = ?; + """; + + private final PreparedStatement statement; + + public GetSchedulingGoalAction(final Connection connection) throws SQLException { + this.statement = connection.prepareStatement(sql); + } + + public Optional get(final GoalId goalId) throws SQLException { + this.statement.setLong(1, goalId.id()); + this.statement.setLong(2, goalId.revision()); + final var resultSet = this.statement.executeQuery(); + + if (!resultSet.next()) return Optional.empty(); + + final var name = resultSet.getString("name"); + final var definition = resultSet.getString("definition"); + final var type = resultSet.getString("type"); + final var path = resultSet.getString("path"); + + return Optional.of(new GoalRecord( + goalId, + name, + type.equals("JAR") ? new GoalType.JAR(Path.of(path), "" /* TODO this is a property of the specification, not the goal */) : new GoalType.EDSL(definition), + true // TODO this is not a property of the goal, but rather of the specification + )); + } + + @Override + public void close() throws SQLException { + this.statement.close(); + } +} diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/PostgresSpecificationRepository.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/PostgresSpecificationRepository.java index b09b690abd..5f97edc0bc 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/PostgresSpecificationRepository.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/PostgresSpecificationRepository.java @@ -1,6 +1,9 @@ package gov.nasa.jpl.aerie.scheduler.server.remotes.postgres; +import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; +import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSchedulingGoalException; import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSpecificationException; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; import gov.nasa.jpl.aerie.scheduler.server.models.SchedulingConditionRecord; import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; import gov.nasa.jpl.aerie.scheduler.server.models.PlanId; @@ -72,4 +75,28 @@ public SpecificationRevisionData getSpecificationRevisionData(final Specificatio throw new DatabaseException("Failed to get scheduling specification revision data", ex); } } + + @Override + public GoalRecord getGoal(final GoalId goalId) throws NoSuchSchedulingGoalException { + try (final var connection = this.dataSource.getConnection()) { + try (final var getGoalAction = new GetSchedulingGoalAction(connection)) { + return getGoalAction + .get(goalId) + .orElseThrow(() -> new NoSuchSchedulingGoalException(goalId)); + } + } catch (final SQLException ex) { + throw new DatabaseException("Failed to get scheduling specification revision data", ex); + } + } + + @Override + public void updateGoalParameterSchema(final GoalId goalId, final ValueSchema schema) { + try (final var connection = this.dataSource.getConnection()) { + try (final var getGoalAction = new UpdateSchedulingGoalParameterSchemaAction(connection)) { + getGoalAction.update(goalId, schema); + } + } catch (final SQLException ex) { + throw new DatabaseException("Failed to get scheduling specification revision data", ex); + } + } } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java new file mode 100644 index 0000000000..b5d3bcfdb6 --- /dev/null +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java @@ -0,0 +1,38 @@ +package gov.nasa.jpl.aerie.scheduler.server.remotes.postgres; + +import gov.nasa.jpl.aerie.merlin.driver.json.ValueSchemaJsonParser; +import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; +import org.intellij.lang.annotations.Language; + +import java.nio.file.Path; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Optional; + +/*package-local*/ final class UpdateSchedulingGoalParameterSchemaAction implements AutoCloseable { + private final @Language("SQL") String sql = """ + update scheduler.scheduling_goal_definition gd set parameter_schema=?::jsonb where gd.goal_id = ? and gd.revision = ?; + """; + + private final PreparedStatement statement; + + public UpdateSchedulingGoalParameterSchemaAction(final Connection connection) throws SQLException { + this.statement = connection.prepareStatement(sql); + } + + public void update(final GoalId goalId, final ValueSchema schema) throws SQLException { + this.statement.setString(1, new ValueSchemaJsonParser().unparse(schema).toString()); + this.statement.setLong(2, goalId.id()); + this.statement.setLong(3, goalId.revision()); + this.statement.executeUpdate(); + } + + @Override + public void close() throws SQLException { + this.statement.close(); + } +} diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java index a4716ca025..63c06b0efd 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java @@ -1,12 +1,24 @@ package gov.nasa.jpl.aerie.scheduler.server.services; +import gov.nasa.jpl.aerie.merlin.driver.DirectiveTypeRegistry; +import gov.nasa.jpl.aerie.merlin.driver.MissionModelLoader; +import gov.nasa.jpl.aerie.merlin.protocol.model.ModelType; +import gov.nasa.ammos.aerie.procedural.scheduling.ProcedureMapper; +import gov.nasa.jpl.aerie.scheduler.ProcedureLoader; +import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSchedulingGoalException; import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSpecificationException; import gov.nasa.jpl.aerie.scheduler.server.exceptions.SpecificationLoadException; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import gov.nasa.jpl.aerie.scheduler.server.models.Specification; import gov.nasa.jpl.aerie.scheduler.server.models.SpecificationId; import gov.nasa.jpl.aerie.scheduler.server.remotes.SpecificationRepository; import gov.nasa.jpl.aerie.scheduler.server.remotes.postgres.SpecificationRevisionData; +import java.nio.file.Path; +import java.util.HashMap; + public record SpecificationService(SpecificationRepository specificationRepository) { // Queries public Specification getSpecification(final SpecificationId specificationId) @@ -20,4 +32,28 @@ public SpecificationRevisionData getSpecificationRevisionData(final Specificatio { return specificationRepository.getSpecificationRevisionData(specificationId); } + + public void refreshSchedulingProcedureParameterTypes(long goalId, long revision) { + final GoalRecord goal; + try { + goal = specificationRepository.getGoal(new GoalId(goalId, revision)); + } catch (NoSuchSchedulingGoalException e) { + throw new RuntimeException(e); + } + switch (goal.type()) { + case GoalType.EDSL edsl -> { + // Do nothing + } + case GoalType.JAR jar -> { + final ProcedureMapper mapper; + try { + mapper = ProcedureLoader.loadProcedure(Path.of("/usr/src/app/merlin_file_store", jar.path().toString())); + } catch (ProcedureLoader.ProcedureLoadException e) { + throw new RuntimeException(e); + } + final var schema = mapper.valueSchema(); + specificationRepository.updateGoalParameterSchema(new GoalId(goalId, revision), schema); + } + } + } } diff --git a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockSpecificationRepository.java b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockSpecificationRepository.java index 63ca06938c..058266be50 100644 --- a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockSpecificationRepository.java +++ b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockSpecificationRepository.java @@ -2,7 +2,11 @@ import java.util.Map; import java.util.Optional; + +import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSpecificationException; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; import gov.nasa.jpl.aerie.scheduler.server.models.Specification; import gov.nasa.jpl.aerie.scheduler.server.models.SpecificationId; import gov.nasa.jpl.aerie.scheduler.server.remotes.SpecificationRepository; @@ -32,4 +36,14 @@ public SpecificationRevisionData getSpecificationRevisionData(final Specificatio final var spec = specifications.get(specificationId); return new SpecificationRevisionData(spec.specificationRevision(), spec.planRevision()); } + + @Override + public GoalRecord getGoal(final GoalId goalId) { + return null; + } + + @Override + public void updateGoalParameterSchema(final GoalId goalId, final ValueSchema schema) { + + } } From 43cedae1b0ee78d5363e2b34759825707bc9b040 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Wed, 7 Aug 2024 06:27:58 -0700 Subject: [PATCH 025/108] Implement annotation processor for scheduling procedures --- procedural/processor/build.gradle | 42 ++ .../processor/AutoValueMappers.java | 339 ++++++++++++++ .../aerie/procedural/processor/Resolver.java | 226 ++++++++++ .../SchedulingProcedureProcessor.java | 284 ++++++++++++ .../procedural/processor/TypePattern.java | 421 ++++++++++++++++++ .../aerie/procedural/processor/TypeRule.java | 36 ++ .../gradle/incremental.annotation.processors | 1 + .../javax.annotation.processing.Processor | 1 + .../scheduling/annotations/AllMappers.java | 12 + .../annotations/SchedulingProcedure.java | 5 + .../scheduling/annotations/WithMappers.java | 14 + settings.gradle | 1 + 12 files changed, 1382 insertions(+) create mode 100644 procedural/processor/build.gradle create mode 100644 procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/AutoValueMappers.java create mode 100644 procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/Resolver.java create mode 100644 procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java create mode 100644 procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/TypePattern.java create mode 100644 procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/TypeRule.java create mode 100644 procedural/processor/src/main/resources/META-INF/gradle/incremental.annotation.processors create mode 100644 procedural/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor create mode 100644 procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/AllMappers.java create mode 100644 procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/SchedulingProcedure.java create mode 100644 procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/WithMappers.java diff --git a/procedural/processor/build.gradle b/procedural/processor/build.gradle new file mode 100644 index 0000000000..405af8f39f --- /dev/null +++ b/procedural/processor/build.gradle @@ -0,0 +1,42 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'io.github.goooler.shadow' version '8.1.7' +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +dependencies { + implementation project(':merlin-sdk') + implementation project(':merlin-framework') + implementation project(':contrib') + implementation project(':procedural:scheduling') + implementation 'org.apache.commons:commons-lang3:3.13.0' + implementation 'com.squareup:javapoet:1.13.0' +} + +publishing { + publications { + library(MavenPublication) { + version = findProperty('publishing.version') + from components.java + } + } + + publishing { + repositories { + maven { + name = findProperty("publishing.name") + url = findProperty("publishing.url") + credentials { + username = System.getenv(findProperty("publishing.usernameEnvironmentVariable")) + password = System.getenv(findProperty("publishing.passwordEnvironmentVariable")) + } + } + } + } +} diff --git a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/AutoValueMappers.java b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/AutoValueMappers.java new file mode 100644 index 0000000000..b18232fc3f --- /dev/null +++ b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/AutoValueMappers.java @@ -0,0 +1,339 @@ +package gov.nasa.ammos.aerie.procedural.processor; + +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.CodeBlock; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; +import com.squareup.javapoet.TypeVariableName; +import gov.nasa.jpl.aerie.contrib.serialization.mappers.RecordValueMapper; +import gov.nasa.jpl.aerie.merlin.framework.Result; +import gov.nasa.jpl.aerie.merlin.framework.ValueMapper; +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; +import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Parameterizable; +import javax.lang.model.element.RecordComponentElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class AutoValueMappers { + static TypeRule recordTypeRule(final Element autoValueMapperElement, final ClassName generatedClassName) { + if (!autoValueMapperElement.getKind().equals(ElementKind.RECORD)) { + throw new RuntimeException("SchedulingProcedures must be records"); + } + + final var componentsAndMappers = getComponentsAndMappers(autoValueMapperElement); + return new TypeRule( + new TypePattern.ClassPattern( + ClassName.get(ValueMapper.class), + List.of(TypePattern.from(autoValueMapperElement.asType()))), + Set.of(), + componentsAndMappers.mappers() + .stream() + .map(mapper -> (TypePattern) new TypePattern.ClassPattern( + ClassName.get(ValueMapper.class), + List.of(mapper.typePattern().box()))) + .toList(), + generatedClassName, + ClassName.get((TypeElement) autoValueMapperElement).canonicalName().replace(".", "_")); + } + + static JavaFile generateAutoValueMappers(final ClassName typeName, final Iterable recordTypes, final Iterable annotationTypes) { + final var builder = + TypeSpec + .classBuilder(typeName) + .addAnnotation( + AnnotationSpec + .builder(javax.annotation.processing.Generated.class) + .addMember("value", "$S", SchedulingProcedureProcessor.class.getCanonicalName()) + .build()) + .addAnnotation( + AnnotationSpec + .builder(SuppressWarnings.class) + .addMember("value", "$S", "unchecked") + .build()) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL); + + for (final var record : recordTypes) { + final var methodName = ClassName.get((TypeElement) record).canonicalName().replace(".", "_"); + final var componentMappers = getComponentsAndMappers(record); + + builder.addMethod( + MethodSpec + .methodBuilder(methodName) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addTypeVariables(((TypeElement) record).getTypeParameters().stream().map(TypeVariableName::get).toList()) + .returns(ParameterizedTypeName.get(ClassName.get(ValueMapper.class), TypeName.get(record.asType()))) + .addParameters( + componentMappers + .mappers() + .stream() + .map(mapper -> ParameterSpec + .builder( + ParameterizedTypeName.get( + ClassName.get(ValueMapper.class), + mapper.typePattern().render().box()), + mapper.identifier(), + Modifier.FINAL) + .build()) + .toList()) + .addCode( + CodeBlock + .builder() + .add("return new $T<>($>\n", ClassName.get(RecordValueMapper.class)) + .add("$L$T.class,\n", + // SAFETY: This cast cannot fail - it merely exists to compensate for an inability + // to express a MyRecord.class. Instead, we use MyRecord.class, and cast it, via + // Object, to a `Class>`. We need to go via Object because Java generics + // do not allow casting a Foo to a Foo> - since Bar may be a container + // that already contains non-Baz objects. In this case, since MyRecord.class is not + // a container, the Java type checker is being overly conservative + castIfGeneric(record), + ClassName.get((TypeElement) record)) + .add("$T.of($>\n", List.class) + .add(CodeBlock.join( + componentMappers.components() + .stream() + .map(recordComponent -> + CodeBlock + .builder() + .add( + "new $T<>($>\n" + "$S,\n" + "$T::$L,\n" + "$L" + "$<)", + RecordValueMapper.Component.class, + recordComponent.componentName(), + ClassName.get((TypeElement) record), + recordComponent.componentName(), + recordComponent.mapperIdentifier()) + .build()) + .toList(), + ",\n")) + .add("$<)$<);") + .build()) + .build()); + } + + record Property(String name, TypeMirror type, String mapperName) {} + for (final var annotation : annotationTypes) { + final var methodName = ClassName.get((TypeElement) annotation).canonicalName().replace(".", "_"); + final var properties = new ArrayList(); + final var necessaryMappers = new HashMap(); + for (final var element : annotation.getEnclosedElements()) { + if (!(element.getKind() == ElementKind.METHOD)) continue; + final var typeMirror = ((ExecutableElement) element).getReturnType(); + final var elementName = element.toString().replace("(", "").replace(")", ""); + final var valueMapperIdentifier = getIdentifier(typeMirror.toString()) + "ValueMapper"; + properties.add(new Property(elementName, typeMirror, valueMapperIdentifier)); + necessaryMappers.put(typeMirror, valueMapperIdentifier); + } + + builder.addMethod( + MethodSpec + .methodBuilder(methodName) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(ParameterizedTypeName.get(ClassName.get(ValueMapper.class), TypeName.get(annotation.asType()))) + .addParameters( + necessaryMappers + .entrySet() + .stream() + .map(mapperRequest -> ParameterSpec + .builder( + ParameterizedTypeName.get( + ClassName.get(ValueMapper.class), + ClassName.get(mapperRequest.getKey()).box()), + mapperRequest.getValue(), + Modifier.FINAL) + .build()) + .toList()) + .addCode( + CodeBlock + .builder() + .add("return $L;\n", + TypeSpec + .anonymousClassBuilder(CodeBlock.of("")) + .addSuperinterface( + ParameterizedTypeName.get(ClassName.get(ValueMapper.class), TypeName.get(annotation.asType()))) + .addMethod( + MethodSpec + .methodBuilder("getValueSchema") + .addModifiers(Modifier.PUBLIC) + .returns(ValueSchema.class) + .addStatement("final var schema = new $T<$T, $T>()", HashMap.class, String.class, ValueSchema.class) + .addCode( + properties + .stream() + .map($ -> CodeBlock.builder().addStatement("schema.put($S, $L.getValueSchema())", $.name, $.mapperName)) + .reduce((x, y) -> x.add("$L", y.build())) + .orElse(CodeBlock.builder()) + .build()) + .addStatement("return $T.ofStruct(schema)", ValueSchema.class) + .build()) + .addMethod( + MethodSpec + .methodBuilder("deserializeValue") + .addModifiers(Modifier.PUBLIC) + .addParameter(SerializedValue.class, "value") + .returns( + ParameterizedTypeName.get( + ClassName.get(Result.class), + TypeName.get(annotation.asType()), + ClassName.get(String.class))) + .addStatement( + "return $T.success($L)", + Result.class, + TypeSpec + .anonymousClassBuilder("") + .addSuperinterface(annotation.asType()) + .addMethod( + MethodSpec + .methodBuilder("annotationType") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .returns(ParameterizedTypeName.get( + ClassName.get(Class.class), + TypeName.get(annotation.asType()))) + .addStatement("return $T.class", annotation.asType()) + .build()) + .addMethods( + properties + .stream() + .map($ -> + MethodSpec + .methodBuilder($.name) + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .returns(TypeName.get($.type)) + .addStatement( + "return $L.deserializeValue($L.asMap().get().get($S)).getSuccessOrThrow()", + $.mapperName, + "value", + $.name) + .build()) + .toList()) + .build()) + .build()) + .addMethod( + MethodSpec + .methodBuilder("serializeValue") + .addModifiers(Modifier.PUBLIC) + .addParameter(ClassName.get(annotation.asType()), "value") + .returns(SerializedValue.class) + .addStatement( + "return $T.of($T.of($L))", + SerializedValue.class, + Map.class, + String.join( + ",", + properties + .stream() + .map($ -> CodeBlock.of("$S, $L.serializeValue($L.$L())", $.name, $.mapperName, "value", $.name).toString()) + .toList()) + ) + .build()) + .build()) + .build()) + .build()); + } + + return JavaFile + .builder(typeName.packageName(), builder.build()) + .skipJavaLangImports(true) + .build(); + } + + private static CodeBlock castIfGeneric(final Element record) { + final var typeParameters = ((Parameterizable) record).getTypeParameters(); + if (typeParameters.isEmpty()) { + return CodeBlock.of(""); + } else { + return CodeBlock.of("(Class<$T>) (Object) ", TypeName.get(record.asType())); + } + } + + /** + * Turn a given string into a valid Java identifier + * @param str a string that may or may not be a valid Java identifier + * @return a string that is a valid Java identifier + * + * Any illegal characters are replaced by their UTF_8 integer representation as a decimal number surrounded by underscores. + * This helps ensure that strings the differ only in illegal characters get unique identifiers. (e.g. "m/s" and "m*s") + * + * E.g. " components, List mappers) {} + + /** + * Correlates the name of a component with the parameter name of its corresponding mapper. + * @param componentName the name of the record component (i.e. the "field name") + * @param mapperIdentifier the identifier which will hold the mapper corresponding to this field + */ + record ComponentMapper(String componentName, String mapperIdentifier) {} + + /** + * Associates the identifier of a mapper with its TypePattern. + * @param identifier the unique identifier for this mapper. + * @param typePattern the type pattern describing this mapper. + */ + record MapperType(String identifier, TypePattern typePattern) {} + + /** + * Given a Record element, generate the parameter names and TypePatterns corresponding + * to its mapper dependencies. + * + * @param element A Record element + * @return enough information to 1) call the record constructor, in order, with the right types, and 2) define a method with the list of required value mappers, and 3) generate a call to that method + */ + private static ComponentsAndMappers getComponentsAndMappers(final Element element) { + final var components = new ArrayList(); + final var mappers = new ArrayList(); + for (final var enclosedElement : element.getEnclosedElements()) { + final var componentIdentifier = getIdentifier(enclosedElement.getSimpleName().toString()); + if (!(enclosedElement instanceof RecordComponentElement el)) continue; + final TypeMirror typeMirror = el.getAccessor().getReturnType(); + final var typePattern = TypePattern.from(typeMirror).box(); + final var mapperIdentifier = componentIdentifier + "_ValueMapper"; + mappers.add(new MapperType(mapperIdentifier, typePattern)); + components.add(new ComponentMapper(componentIdentifier, mapperIdentifier)); + } + return new ComponentsAndMappers(components, mappers); + } +} diff --git a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/Resolver.java b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/Resolver.java new file mode 100644 index 0000000000..eb8167b29d --- /dev/null +++ b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/Resolver.java @@ -0,0 +1,226 @@ +package gov.nasa.ammos.aerie.procedural.processor; + +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.CodeBlock; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; +import com.squareup.javapoet.WildcardTypeName; +import gov.nasa.jpl.aerie.merlin.framework.ValueMapper; +import gov.nasa.ammos.aerie.procedural.processor.TypePattern.ClassPattern; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.PrimitiveType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public final class Resolver { + private final Types typeUtils; + private final Elements elementUtils; + private final List typeRules; + + public Resolver(final Types typeUtils, final Elements elementUtils, final List typeRules) { + this.typeUtils = Objects.requireNonNull(typeUtils); + this.elementUtils = Objects.requireNonNull(elementUtils); + this.typeRules = Objects.requireNonNull(typeRules); + } + + public Optional instantiateMapperFor(final TypeMirror mirror) { + final var mapperCode = applyRules(createInitialGoal(mirror)); + + // TODO: Do away with this null-checking stuff, somehow. + if (mirror.getKind() == TypeKind.DECLARED || mirror.getKind() == TypeKind.ARRAY) { + return mapperCode.map($ -> CodeBlock.of("$L", $)); + } else { + return mapperCode; + } + } + + public Optional instantiateNullableMapperFor(final TypeMirror mirror) { + final var mapperCode = applyRules(createInitialGoal(mirror)); + + // TODO: Do away with this null-checking stuff, somehow. + if (mirror.getKind() == TypeKind.DECLARED || mirror.getKind() == TypeKind.ARRAY) { + return mapperCode.map($ -> CodeBlock.of( + "new $T<>(\n$>$>$L$<$<)", + gov.nasa.jpl.aerie.contrib.serialization.mappers.NullableValueMapper.class, + $)); + } else { + return mapperCode; + } + } + + private TypePattern createInitialGoal(final TypeMirror mirror) { + final List mapperArguments; + if (mirror.getKind().isPrimitive()) { + var typePattern = TypePattern.from(typeUtils.boxedClass((PrimitiveType) mirror).asType()); + final var annotations = new LinkedList<>(mirror.getAnnotationMirrors()); + while (!annotations.isEmpty()) { + final AnnotationMirror annotation = annotations.removeLast(); + final var className = ClassName.get((TypeElement) annotation.getAnnotationType().asElement()); + typePattern = new TypePattern.AnnotationPattern(className, Optional.of(annotation), typePattern); + } + mapperArguments = List.of(typePattern); + } else { + mapperArguments = List.of(TypePattern.from(mirror)); + } + + return new ClassPattern(ClassName.get(ValueMapper.class), mapperArguments); + } + + public Optional applyRules(final TypePattern goal) { + if (goal instanceof ClassPattern && ((ClassPattern)goal).name().equals(ClassName.get(Class.class))) { + final var pattern = ((ClassPattern)goal).arguments().get(0); + return Optional.of( + (pattern.render().equals(pattern.erasure())) + ? CodeBlock.of("$T.class", pattern.erasure()) + : CodeBlock.of("(Class<$T>) (Object) $T.class", pattern.render(), pattern.erasure())); + } else { + for (final var rule : this.typeRules) { + final var codeBlock = this.applyRule(rule, goal); + if (codeBlock.isPresent()) return codeBlock; + } + } + return Optional.empty(); + } + + private Optional applyRule(final TypeRule rule, final TypePattern goal) { + // Extract type bindings for the type parameters of the rule + final Map typeMapping; + try { + typeMapping = rule.head().match(goal); + } catch (TypePattern.UnificationException e) { + return Optional.empty(); + } + + // Ensure the match satisfies the rule's type parameter bounds + for (final var name : rule.enumBoundedTypeParameters()) { + final var pattern = typeMapping.get(name); + if (pattern == null) return Optional.empty(); + if (!(pattern instanceof ClassPattern)) return Optional.empty(); + + // Only enum bounds are supported, but could expand at some point. + // Supporting value mapper resolvers for types like: + // - `List` or + // - `List>` + // is not straightforward. + final var patternType = elementUtils.getTypeElement(pattern.erasure().toString()).asType(); + final var enumType = typeUtils.erasure(elementUtils.getTypeElement("java.lang.Enum").asType()); + if (!typeUtils.isSubtype(patternType, enumType)) return Optional.empty(); + } + + final var annotationValues = new HashMap(); + for (final var annotation : collectAnnotationParameters(goal)) { + final var className = ClassName.get((TypeElement) annotation.getAnnotationType().asElement()); + annotationValues.put(className, annotation); + } + + // Satisfy the subgoals of the rule + final var dependencies = new ArrayList(rule.parameters().size()); + if (rule.name().isPresent()) { + dependencies.add(CodeBlock.of("$S", rule.name().get())); + } + for (final var parameter : rule.parameters()) { + if (parameter instanceof ClassPattern p && annotationValues.containsKey(p.name())) { + dependencies.add(CodeBlock.of("$L", generateAnnotationInstance(annotationValues.get(p.name())))); + } else { + final var codeBlock = applyRules(parameter.substitute(typeMapping)); + if (codeBlock.isEmpty()) return Optional.empty(); + dependencies.add(codeBlock.get()); + } + } + + // Build a codeblock satisfying the goal of this rule + final var builder = CodeBlock.builder(); + builder.add("$T.$L(", rule.factory(), rule.method()); + if (dependencies.size() > 0) { + final var iter = dependencies.iterator(); + builder.add("\n$>$>$L$<$<", iter.next()); + while (iter.hasNext()) { + builder.add(",\n$>$>$L$<$<", iter.next()); + } + } + builder.add(")"); + return Optional.of(builder.build()); + } + + List collectAnnotationParameters(TypePattern pattern) { + final var result = new ArrayList(); + if (pattern instanceof TypePattern.AnnotationPattern p) { + result.add(p.payload().get()); + result.addAll(collectAnnotationParameters(p.target())); + } else if (pattern instanceof TypePattern.PrimitivePattern p) { + // Do nothing + } else if (pattern instanceof TypePattern.ClassPattern p) { + for (final var argument : p.arguments()) { + result.addAll(collectAnnotationParameters(argument)); + } + } else if (pattern instanceof TypePattern.ArrayPattern p) { + result.addAll(collectAnnotationParameters(p.element())); + } else if (pattern instanceof TypePattern.TypeVariablePattern p) { + // Do nothing + } else { + throw new Error("unhandled subtype of " + TypePattern.class.getCanonicalName() + ": " + pattern); + } + return result; + } + + private TypeSpec generateAnnotationInstance(final AnnotationMirror annotation) { + final var annotationType = annotation.getAnnotationType(); + final var builder = TypeSpec + .anonymousClassBuilder("") + .superclass(annotationType) + .addMethod( + MethodSpec + .methodBuilder("annotationType") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .returns(ParameterizedTypeName.get( + ClassName.get(Class.class), + WildcardTypeName.subtypeOf(TypeName.get(annotationType)))) + .addStatement(CodeBlock.of("return $T.class", annotationType)) + .build()); + for (final var enclosedElement : annotation.getAnnotationType().asElement().getEnclosedElements()) { + if (!(enclosedElement instanceof ExecutableElement el)) continue; + final String methodName = el.getSimpleName().toString(); + builder + .addMethod( + MethodSpec + .methodBuilder(methodName) + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .returns(TypeName.get(el.getReturnType())) + .addStatement(CodeBlock.of("return $L", + getAnnotationAttribute(annotation, methodName) + .orElse(el.getDefaultValue()) + .toString())) + .build()); + } + return builder.build(); + } + + private static Optional getAnnotationAttribute(final AnnotationMirror annotationMirror, final String attributeName) + { + for (final var entry : annotationMirror.getElementValues().entrySet()) { + if (Objects.equals(attributeName, entry.getKey().getSimpleName().toString())) { + return Optional.of(entry.getValue()); + } + } + + return Optional.empty(); + } +} diff --git a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java new file mode 100644 index 0000000000..d8dd19bf43 --- /dev/null +++ b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java @@ -0,0 +1,284 @@ +package gov.nasa.ammos.aerie.procedural.processor; + +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; +import gov.nasa.jpl.aerie.merlin.framework.ValueMapper; +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; +import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; +import gov.nasa.ammos.aerie.procedural.scheduling.ProcedureMapper; +import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure; +import gov.nasa.ammos.aerie.procedural.scheduling.annotations.WithMappers; + +import javax.annotation.processing.Completion; +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import javax.tools.Diagnostic; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.annotation.Repeatable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +public final class SchedulingProcedureProcessor implements Processor { + // Effectively final, late-initialized + private Messager messager = null; + private Filer filer = null; + private Elements elementUtils = null; + private Types typeUtils = null; + + @Override + public Set getSupportedOptions() { + return Set.of(); + } + + /** Elements marked by these annotations will be treated as processing roots. */ + @Override + public Set getSupportedAnnotationTypes() { + return Set.of(SchedulingProcedure.class.getCanonicalName(), WithMappers.class.getCanonicalName()); + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + @Override + public void init(final ProcessingEnvironment processingEnv) { + this.messager = processingEnv.getMessager(); + this.filer = processingEnv.getFiler(); + this.elementUtils = processingEnv.getElementUtils(); + this.typeUtils = processingEnv.getTypeUtils(); + } + + @Override + public boolean process(final Set annotations, final RoundEnvironment roundEnv) { + final var mapperClassElements = new ArrayList(); + PackageElement packageElement = null; + for (final var packageElement$ : roundEnv.getElementsAnnotatedWith(WithMappers.class)) { + if (packageElement != null) throw new RuntimeException("Multiple packages annotated with WithMappers"); + if (packageElement$.getKind() != ElementKind.PACKAGE) throw new RuntimeException("Only packages can be annotated with WithMappers"); + packageElement = (PackageElement) packageElement$; + } + if (packageElement == null) return false; //throw new RuntimeException("Need to annotate a package-info class with WithMappers"); + + for (final var withMappersAnnotation : getRepeatableAnnotation(packageElement, WithMappers.class)) { + final var attribute = getAnnotationAttribute(withMappersAnnotation, "value").orElseThrow(); + + if (!(attribute.getValue() instanceof DeclaredType)) { + throw new RuntimeException( + "Mappers class not yet defined " + + packageElement + + withMappersAnnotation + + attribute); + } + + mapperClassElements.add((TypeElement) ((DeclaredType) attribute.getValue()).asElement()); + } + + final var typeRules = new ArrayList(); + for (final var factory : mapperClassElements) { + typeRules.addAll(parseValueMappers(factory)); + } + + final var procedures = roundEnv.getElementsAnnotatedWith(SchedulingProcedure.class); + + final var generatedClassName = ClassName.get(packageElement.getQualifiedName() + ".generated", "AutoValueMappers"); + for (final var procedure : procedures) { + final var procedureElement = (TypeElement) procedure; + typeRules.add(AutoValueMappers.recordTypeRule(procedureElement, generatedClassName)); + } + + final var generatedFiles = new ArrayList(); + + generatedFiles.add(AutoValueMappers.generateAutoValueMappers(generatedClassName, procedures, List.of())); + + // For each procedure, generate a file that implements Procedure, Supplier + for (final var procedure : procedures) { + final TypeName procedureType = TypeName.get(procedure.asType()); + final ParameterizedTypeName valueMapperType = ParameterizedTypeName.get( + ClassName.get(ValueMapper.class), + procedureType); + + final var valueMapperCode = new Resolver(typeUtils, elementUtils, typeRules).applyRules(new TypePattern.ClassPattern(ClassName.get(ValueMapper.class), List.of(new TypePattern.ClassPattern((ClassName) procedureType, List.of())))); + if (valueMapperCode.isEmpty()) throw new Error("Could not generate a valuemapper for procedure " + procedure.getSimpleName()); + + + generatedFiles.add(JavaFile + .builder(generatedClassName.packageName() + ".procedures", TypeSpec + .classBuilder(procedure.getSimpleName().toString()) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addSuperinterface(ParameterizedTypeName.get(ClassName.get(ProcedureMapper.class), procedureType)) + .addMethod(MethodSpec + .methodBuilder("valueSchema") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .returns(ValueSchema.class) + .addStatement("return $L.getValueSchema()", valueMapperCode.get()) + .build()) + .addMethod(MethodSpec + .methodBuilder("serialize") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .addParameter(procedureType, "procedure") + .returns(SerializedValue.class) + .addStatement("return $L.serializeValue(procedure)", valueMapperCode.get()) + .build()) + .addMethod(MethodSpec + .methodBuilder("deserialize") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .addParameter(SerializedValue.class, "value") + .returns(procedureType) + .addStatement("return $L.deserializeValue(value).getSuccessOrThrow(e -> new $T(e))", valueMapperCode.get(), RuntimeException.class) + .build()) + .build()) + .skipJavaLangImports(true) + .build()); + } + + for (final var generatedFile : generatedFiles) { + this.messager.printMessage( + Diagnostic.Kind.NOTE, + "Generating " + generatedFile.packageName + "." + generatedFile.typeSpec.name); + try { + generatedFile.writeTo(this.filer); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + // Allow other annotation processors to process the framework annotations. + return false; + + } + + @Override + public Iterable getCompletions( + final Element element, + final AnnotationMirror annotation, + final ExecutableElement member, + final String userText) + { + return Collections::emptyIterator; + } + + private static Optional getAnnotationAttribute(final AnnotationMirror annotationMirror, final String attributeName) + { + for (final var entry : annotationMirror.getElementValues().entrySet()) { + if (Objects.equals(attributeName, entry.getKey().getSimpleName().toString())) { + return Optional.of(entry.getValue()); + } + } + + return Optional.empty(); + } + + private List getRepeatableAnnotation(final Element element, final Class annotationClass) + { + final var containerClass = annotationClass.getAnnotation(Repeatable.class).value(); + + final var annotationType = this.elementUtils.getTypeElement(annotationClass.getCanonicalName()).asType(); + final var containerType = this.elementUtils.getTypeElement(containerClass.getCanonicalName()).asType(); + + final var mirrors = new ArrayList(); + for (final var mirror : element.getAnnotationMirrors()) { + if (this.typeUtils.isSameType(annotationType, mirror.getAnnotationType())) { + mirrors.add(mirror); + } else if (this.typeUtils.isSameType(containerType, mirror.getAnnotationType())) { + // SAFETY: a container annotation has a value() attribute that is an array of annotations + @SuppressWarnings("unchecked") + final var containedMirrors = + (List) + getAnnotationAttribute(mirror, "value") + .orElseThrow() + .getValue(); + + mirrors.addAll(containedMirrors); + } + } + + return mirrors; + } + + private List parseValueMappers(final TypeElement factory) { + final var rules = new ArrayList(); + + for (final var element : factory.getEnclosedElements()) { + if (element.getKind().equals(ElementKind.METHOD)) { + rules.add(this.parseValueMapperMethod((ExecutableElement) element, ClassName.get(factory))); + } + } + return rules; + } + + private TypeRule parseValueMapperMethod(final ExecutableElement element, final ClassName factory) { + if (!element.getModifiers().containsAll(Set.of(Modifier.PUBLIC, Modifier.STATIC))) { + throw new RuntimeException( + "Value Mapper method must be public and static " + + element + ); + } + + final var head = TypePattern.from(element.getReturnType()); + final var enumBoundedTypeParameters = getEnumBoundedTypeParameters(element); + final var method = element.getSimpleName().toString(); + final var parameters = new ArrayList(); + for (final var parameter : element.getParameters()) { + parameters.add(TypePattern.from(parameter)); + } + + return new TypeRule(head, enumBoundedTypeParameters, parameters, factory, method); + } + + private Set getEnumBoundedTypeParameters(final ExecutableElement element) { + final var enumBoundedTypeParameters = new HashSet(); + // Ensure type parameters are unbounded or bounded only by enum type. + // Supporting value mapper resolvers for types like: + // - `List` or + // - `List>` + // is not straightforward. + for (final var typeParameter : element.getTypeParameters()) { + final var bounds = typeParameter.getBounds(); + for (final var bound : bounds) { + final var erasure = typeUtils.erasure(bound); + final var objectType = elementUtils.getTypeElement("java.lang.Object").asType(); + final var enumType = typeUtils.erasure(elementUtils.getTypeElement("java.lang.Enum").asType()); + if (typeUtils.isSameType(erasure, objectType)) { + // Nothing to do + } else if (typeUtils.isSameType(erasure, enumType)) { + enumBoundedTypeParameters.add(typeParameter.getSimpleName().toString()); + } else { + throw new RuntimeException( + "Value Mapper method type parameter must be unbounded, or bounded by enum type only" + element + ); + } + } + } + return enumBoundedTypeParameters; + } +} diff --git a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/TypePattern.java b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/TypePattern.java new file mode 100644 index 0000000000..280e51d273 --- /dev/null +++ b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/TypePattern.java @@ -0,0 +1,421 @@ +package gov.nasa.ammos.aerie.procedural.processor; + +import com.squareup.javapoet.ArrayTypeName; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeVariableName; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public sealed interface TypePattern { + static TypePattern from(final TypeMirror mirror) { + TypePattern result; + switch (mirror.getKind()) { + case BOOLEAN -> result = new PrimitivePattern(Primitive.BOOLEAN); + case BYTE -> result = new PrimitivePattern(Primitive.BYTE); + case SHORT -> result = new PrimitivePattern(Primitive.SHORT); + case INT -> result = new PrimitivePattern(Primitive.INT); + case LONG -> result = new PrimitivePattern(Primitive.LONG); + case CHAR -> result = new PrimitivePattern(Primitive.CHAR); + case FLOAT -> result = new PrimitivePattern(Primitive.FLOAT); + case DOUBLE -> result = new PrimitivePattern(Primitive.DOUBLE); + case ARRAY -> result = new ArrayPattern(TypePattern.from(((ArrayType) mirror).getComponentType())); + case TYPEVAR -> { + var typeVarName = mirror.toString(); + if (typeVarName.contains(" ")) { + typeVarName = typeVarName.substring(typeVarName.lastIndexOf(' ') + 1); + } + result = new TypeVariablePattern(typeVarName); + } + case DECLARED -> { + // DeclaredType element can be cast as TypeElement because it's a Type + final var className = ClassName.get((TypeElement) ((DeclaredType) mirror).asElement()); + final var typeArguments = ((DeclaredType) mirror).getTypeArguments(); + final var argumentPatterns = new ArrayList(typeArguments.size()); + for (final var typeArgument : typeArguments) { + argumentPatterns.add(TypePattern.from(typeArgument)); + } + result = new ClassPattern(className, argumentPatterns); + } + + default -> throw new Error("Cannot construct a pattern for type " + mirror); + } + + final var annotations = mirror.getAnnotationMirrors(); + for (int i = annotations.size() - 1; i >= 0; i--) { + final var annotation = annotations.get(i); + final var className = ClassName.get((TypeElement) annotation.getAnnotationType().asElement()); + result = new AnnotationPattern(className, Optional.of(annotation), result); + } + return result; + } + + static TypePattern from(final VariableElement element) { + return from(element.asType()); + } + + /** + * Given a TypePattern, produce a new TypePattern where all type variables whose names are + * in the given map have been replaced with the corresponding type patterns. + * @param substitution A map from type variable name to the type pattern with which to replace it + * @return a TypePattern with the same structure, but with any matching type variables replaced + * E.g. ClassPattern(List, TypeVariablePattern(T)).substitute(Map.of("T", ClassPattern(Double))) will return + * ClassPattern(List, Double) + */ + TypePattern substitute(Map substitution); + + /** + * Attempt to match this TypePattern against the given other TypePattern. Throw a UnificationException if they cannot be matched + * @param other the ground TypePattern to match against this TypePattern + * @return an assignment of type variables to TypePatterns, if match is successful + * @throws UnificationException if the two patterns do not match + */ + Map match(TypePattern other) throws UnificationException; + + /** + * Whether this TypePattern contains any type variables. Ground is true if there are no type variables present. + * @return false if there is at least one type variable in this TypePattern. true if there are none. + */ + boolean isGround(); + + /** + * Whether this TypePattern has the same structure, same types, and same type variable names as the other pattern + * @param other the other TypePattern to compare with + * @return true if the two TypePatterns are identical, false otherwise. + */ + boolean isSyntacticallyEqualTo(TypePattern other); + + /** + * Represent this TypePattern as a javapoet TypeName + * @return a javapoet TypeName corresponding to this TypePattern + * + * E.g. ClassPattern(List, Double).render() --> ClassName(List, Double) + */ + TypeName render(); + + /** + * Represent this TypePattern as a javapoet TypeName without generics + * @return a javapoet TypeName corresponding to this TypePattern without generics + * + * E.g. ClassPattern(List, Double).erasure() --> ClassName(List) + */ + TypeName erasure(); + + /** + * If this TypePattern represents a primitive, return a TypePattern that represents the corresponding Object type. + * @return a TypePattern that is not a PrimitiveTypePattern + * + * E.g. PrimitivePattern(boolean) --> ClassPattern(Boolean) + * + * There is no need to recurse: if the top level is not a PrimitiveTypePattern, + * the entire type pattern is already considered "boxed" + */ + TypePattern box(); + + class UnificationException extends Exception {} + + record TypeVariablePattern(String name) implements TypePattern { + @Override + public TypePattern substitute(final Map substitution) { + return substitution.getOrDefault(this.name, this); + } + + @Override + public Map match(final TypePattern other) { + assert other.isGround(); + + return Map.of(this.name, other); + } + + @Override + public boolean isGround() { + return false; + } + + @Override + public boolean isSyntacticallyEqualTo(final TypePattern o) { + if (!(o instanceof TypeVariablePattern)) return false; + final var other = (TypeVariablePattern) o; + + return this.name.equals(other.name); + } + + @Override + public TypeName render() { + return TypeVariableName.get(this.name); + } + + @Override + public TypeName erasure() { + return this.render(); + } + + @Override + public TypePattern box() { + return this; + } + } + + record PrimitivePattern(Primitive primitive) implements TypePattern { + @Override + public PrimitivePattern substitute(final Map substitution) { + return this; + } + + @Override + public Map match(final TypePattern other) throws UnificationException { + if (!this.isSyntacticallyEqualTo(other)) throw new UnificationException(); + + return Map.of(); + } + + @Override + public boolean isGround() { + return true; + } + + @Override + public boolean isSyntacticallyEqualTo(final TypePattern o) { + if (!(o instanceof PrimitivePattern)) return false; + final var other = (PrimitivePattern) o; + + return this.primitive.equals(other.primitive); + } + + @Override + public TypeName render() { + return this.primitive.toTypeName(); + } + + @Override + public TypeName erasure() { + return this.render(); + } + + @Override + public TypePattern box() { + return new ClassPattern((ClassName) this.render().box(), List.of()); + } + } + + enum Primitive { + BOOLEAN(TypeName.BOOLEAN), + BYTE(TypeName.BYTE), + SHORT(TypeName.SHORT), + INT(TypeName.INT), + LONG(TypeName.LONG), + CHAR(TypeName.CHAR), + FLOAT(TypeName.FLOAT), + DOUBLE(TypeName.DOUBLE); + + private final TypeName repr; + + Primitive(final TypeName repr) { + this.repr = repr; + } + + public TypeName toTypeName() { + return this.repr; + } + } + + record ArrayPattern(TypePattern element) implements TypePattern { + @Override + public ArrayPattern substitute(final Map substitution) { + final var child = this.element.substitute(substitution); + if (child == this.element) return this; + + return new ArrayPattern(child); + } + + @Override + public Map match(final TypePattern o) throws UnificationException { + if (!(o instanceof ArrayPattern)) throw new UnificationException(); + final var other = (ArrayPattern) o; + + return this.element.match(other.element); + } + + @Override + public boolean isGround() { + return this.element.isGround(); + } + + @Override + public boolean isSyntacticallyEqualTo(final TypePattern o) { + if (!(o instanceof ArrayPattern)) return false; + final var other = (ArrayPattern) o; + + return this.element.isSyntacticallyEqualTo(other.element); + } + + @Override + public TypeName render() { + return ArrayTypeName.of(this.element.render()); + } + + @Override + public TypeName erasure() { + return ArrayTypeName.of(this.element.erasure()); + } + + @Override + public TypePattern box() { + return this; + } + } + + record ClassPattern(ClassName name, List arguments) implements TypePattern { + @Override + public ClassPattern substitute(final Map substitution) { + final var substitutedArguments = new ArrayList(this.arguments.size()); + for (final var argument : this.arguments) { + substitutedArguments.add(argument.substitute(substitution)); + } + + // The `allUnchanged` block can be removed without impacting correctness, + // but it exists to avoid needless extra allocations whenever possible. + var allUnchanged = true; + for (var i = 0; i < this.arguments.size(); i += 1) { + if (this.arguments.get(i) != substitutedArguments.get(i)) { + allUnchanged = false; + break; + } + } + if (allUnchanged) return this; + + return new ClassPattern(this.name, substitutedArguments); + } + + @Override + public Map match(final TypePattern o) throws UnificationException { + if (!(o instanceof ClassPattern)) throw new UnificationException(); + final var other = (ClassPattern) o; + if (!this.name.equals(other.name)) throw new UnificationException(); + if (this.arguments.size() != other.arguments.size()) throw new UnificationException(); + + // This specialization by arity can be removed without impacting correctness, + // but it exists to avoid needless extra work whenever possible. + if (this.arguments.size() == 0) { + return Map.of(); + } else if (this.arguments.size() == 1) { + return this.arguments.get(0).match(other.arguments.get(0)); + } + + final var collectedSubstitution = new HashMap(); + for (var i = 0; i < this.arguments.size(); i += 1) { + final var substitution = this.arguments.get(i).match(other.arguments.get(i)); + + for (final var entry : substitution.entrySet()) { + if (!collectedSubstitution.containsKey(entry.getKey())) { + collectedSubstitution.put(entry.getKey(), entry.getValue()); + continue; + } + + if (collectedSubstitution.get(entry.getKey()).isSyntacticallyEqualTo(entry.getValue())) { + continue; + } + + throw new UnificationException(); + } + } + + return collectedSubstitution; + } + + @Override + public boolean isGround() { + return this.arguments.stream().allMatch(TypePattern::isGround); + } + + @Override + public boolean isSyntacticallyEqualTo(final TypePattern o) { + if (!(o instanceof ClassPattern)) return false; + final var other = (ClassPattern) o; + + if (this.arguments.size() != other.arguments.size()) return false; + + for (var i = 0; i < this.arguments.size(); i += 1) { + if (!this.arguments.get(i).isSyntacticallyEqualTo(other.arguments.get(i))) { + return false; + } + } + + return true; + } + + @Override + public TypeName render() { + if (this.arguments.size() == 0) return this.name; + + final var argumentTypeNames = new TypeName[this.arguments.size()]; + for (var i = 0; i < argumentTypeNames.length; i += 1) { + argumentTypeNames[i] = this.arguments.get(i).render().box(); + } + + return ParameterizedTypeName.get(this.name, argumentTypeNames); + } + + @Override + public TypeName erasure() { + return this.name; + } + + @Override + public TypePattern box() { + return this; + } + } + + record AnnotationPattern(ClassName annotationClassName, Optional payload, TypePattern target) implements TypePattern { + @Override + public AnnotationPattern substitute(final Map substitution) { + return new AnnotationPattern(this.annotationClassName, this.payload, target.substitute(substitution)); + } + + @Override + public Map match(final TypePattern o) throws UnificationException { + if (!(o instanceof final AnnotationPattern other)) throw new UnificationException(); + if (!this.annotationClassName.equals(other.annotationClassName)) throw new UnificationException(); + return this.target.match(other.target); + } + + @Override + public boolean isGround() { + return this.target.isGround(); + } + + @Override + public boolean isSyntacticallyEqualTo(final TypePattern o) { + if (!(o instanceof final AnnotationPattern other)) return false; + if (!this.annotationClassName.equals(other.annotationClassName)) return false; + return this.target.isSyntacticallyEqualTo(other.target); + } + + @Override + public TypeName render() { + return this.target.render(); + } + + @Override + public TypeName erasure() { + return this.render(); + } + + @Override + public TypePattern box() { + return new AnnotationPattern(this.annotationClassName, this.payload, this.target.box()); + } + } +} diff --git a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/TypeRule.java b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/TypeRule.java new file mode 100644 index 0000000000..5fece28bf8 --- /dev/null +++ b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/TypeRule.java @@ -0,0 +1,36 @@ +package gov.nasa.ammos.aerie.procedural.processor; + +import com.squareup.javapoet.ClassName; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public record TypeRule( + TypePattern head, + Set enumBoundedTypeParameters, + List parameters, + ClassName factory, + String method, + Optional name +) { + public TypeRule( + final TypePattern head, + final Set enumBoundedTypeParameters, + final List parameters, + final ClassName factory, + final String method) + { + this(head, enumBoundedTypeParameters, parameters, factory, method, Optional.empty()); + } + public TypeRule( + final TypePattern head, + final Set enumBoundedTypeParameters, + final List parameters, + final ClassName factory, + final String method, + final String name) + { + this(head, enumBoundedTypeParameters, parameters, factory, method, Optional.of(name)); + } +} diff --git a/procedural/processor/src/main/resources/META-INF/gradle/incremental.annotation.processors b/procedural/processor/src/main/resources/META-INF/gradle/incremental.annotation.processors new file mode 100644 index 0000000000..20c58ba7c5 --- /dev/null +++ b/procedural/processor/src/main/resources/META-INF/gradle/incremental.annotation.processors @@ -0,0 +1 @@ +gov.nasa.jpl.aerie.merlin.processor.MissionModelProcessor,aggregating diff --git a/procedural/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/procedural/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000000..13dd2f6a19 --- /dev/null +++ b/procedural/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +gov.nasa.ammos.aerie.procedural.processor.SchedulingProcedureProcessor diff --git a/procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/AllMappers.java b/procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/AllMappers.java new file mode 100644 index 0000000000..324d5d9a58 --- /dev/null +++ b/procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/AllMappers.java @@ -0,0 +1,12 @@ +package gov.nasa.ammos.aerie.procedural.scheduling.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.PACKAGE) +@interface AllMappers { + WithMappers[] value(); +} diff --git a/procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/SchedulingProcedure.java b/procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/SchedulingProcedure.java new file mode 100644 index 0000000000..39cd9163ed --- /dev/null +++ b/procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/SchedulingProcedure.java @@ -0,0 +1,5 @@ +package gov.nasa.ammos.aerie.procedural.scheduling.annotations; + +/***/ +public @interface SchedulingProcedure { +} diff --git a/procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/WithMappers.java b/procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/WithMappers.java new file mode 100644 index 0000000000..b7c79efb6f --- /dev/null +++ b/procedural/scheduling/src/main/java/gov/nasa/ammos/aerie/procedural/scheduling/annotations/WithMappers.java @@ -0,0 +1,14 @@ +package gov.nasa.ammos.aerie.procedural.scheduling.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.PACKAGE) +@Repeatable(AllMappers.class) +public @interface WithMappers { + Class value(); +} diff --git a/settings.gradle b/settings.gradle index 9853b119d3..cbb1a2575b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,6 +13,7 @@ include 'parsing-utilities' include 'permissions' // Procedural post-simulation libraries +include 'procedural:processor' include 'procedural:timeline' include 'procedural:constraints' include 'procedural:scheduling' From 9db34ef6c76a011d4e5069abf6771d83ba1ba8c6 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Tue, 6 Aug 2024 17:29:31 -0700 Subject: [PATCH 026/108] Update procedural constraints to use ActivityId from merlin --- .../gov/nasa/jpl/aerie/merlin/driver/ActivityId.java | 4 ++++ .../jpl/aerie/merlin/driver/ActivityInstanceId.java | 2 +- .../ammos/aerie/procedural/constraints/ActivityId.kt | 7 ------- .../ammos/aerie/procedural/constraints/Violation.kt | 1 + .../ammos/aerie/procedural/constraints/Violations.kt | 11 +++++------ .../gov/nasa/jpl/aerie/types/ActivityDirectiveId.java | 2 +- 6 files changed, 12 insertions(+), 15 deletions(-) create mode 100644 merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityId.java delete mode 100644 procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/ActivityId.kt diff --git a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityId.java b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityId.java new file mode 100644 index 0000000000..fe5ee3d894 --- /dev/null +++ b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityId.java @@ -0,0 +1,4 @@ +package gov.nasa.jpl.aerie.merlin.driver; + +public interface ActivityId { +} diff --git a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityInstanceId.java b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityInstanceId.java index 4d57e7a666..eeb44b6a6c 100644 --- a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityInstanceId.java +++ b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityInstanceId.java @@ -1,3 +1,3 @@ package gov.nasa.jpl.aerie.merlin.driver; -public record ActivityInstanceId(long id) {} +public record ActivityInstanceId(long id) implements ActivityId {} diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/ActivityId.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/ActivityId.kt deleted file mode 100644 index 0554d88556..0000000000 --- a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/ActivityId.kt +++ /dev/null @@ -1,7 +0,0 @@ -package gov.nasa.ammos.aerie.procedural.constraints - -/** An activity ID, referencing either a directive or instance. */ -sealed interface ActivityId { - /***/ data class InstanceId(/***/ val id: Long): ActivityId - /***/ data class DirectiveId(/***/ val id: Long): ActivityId -} diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violation.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violation.kt index 8a396416a4..b3fb5c7cfc 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violation.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violation.kt @@ -2,6 +2,7 @@ package gov.nasa.ammos.aerie.procedural.constraints import gov.nasa.ammos.aerie.procedural.timeline.Interval import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike +import gov.nasa.jpl.aerie.merlin.driver.ActivityId /** A single violation of a constraint. */ data class Violation( diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt index 73fe96dd2d..dea3068dbd 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt @@ -1,7 +1,5 @@ package gov.nasa.ammos.aerie.procedural.constraints -import gov.nasa.ammos.aerie.procedural.constraints.ActivityId.DirectiveId -import gov.nasa.ammos.aerie.procedural.constraints.ActivityId.InstanceId import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline import gov.nasa.ammos.aerie.procedural.timeline.BoundsTransformer import gov.nasa.ammos.aerie.procedural.timeline.Timeline @@ -14,6 +12,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Instance import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList +import gov.nasa.jpl.aerie.merlin.driver.ActivityId /** A timeline of [Violations][Violation]. */ data class Violations(private val timeline: Timeline): @@ -69,11 +68,11 @@ data class Violations(private val timeline: Timeline): l.getActivityId(), r.getActivityId() ) - )} + ) } - private fun > V.getActivityId() = when (this) { - is Instance<*> -> InstanceId(id.id) - is Directive<*> -> DirectiveId(id.id) + private fun > V.getActivityId(): ActivityId? = when (this) { + is Instance<*> -> if (directiveId != null) directiveId else id + is Directive<*> -> id else -> null } } diff --git a/type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityDirectiveId.java b/type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityDirectiveId.java index c264c39dfc..d5ab51d0d0 100644 --- a/type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityDirectiveId.java +++ b/type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityDirectiveId.java @@ -1,3 +1,3 @@ package gov.nasa.jpl.aerie.types; -public record ActivityDirectiveId(long id) {} +public record ActivityDirectiveId(long id) implements ActivityId {} From 4fb64b40d30bacf2cc37f0d80e3c69e0d6e0d97c Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Tue, 6 Aug 2024 16:46:13 -0700 Subject: [PATCH 027/108] Add examples of scheduling procedures and constraints --- procedural/examples/foo-procedures/README.md | 11 +++ .../examples/foo-procedures/build.gradle | 98 +++++++++++++++++++ .../examples/fooprocedures/Helper.java | 7 ++ .../fooprocedures/constraints/ConstFruit.java | 21 ++++ .../fooprocedures/constraints/Hello.java | 9 ++ .../examples/fooprocedures/package-info.java | 5 + .../procedures/SampleProcedure.java | 31 ++++++ .../procedures/SimulationDemo.java | 62 ++++++++++++ .../fooprocedures/constraints/Procedure.java | 4 + .../constraints/ProcedureLoader.java | 68 +++++++++++++ .../constraints/ProcedureLoadingTest.java | 16 +++ settings.gradle | 3 + 12 files changed, 335 insertions(+) create mode 100644 procedural/examples/foo-procedures/README.md create mode 100644 procedural/examples/foo-procedures/build.gradle create mode 100644 procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/Helper.java create mode 100644 procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java create mode 100644 procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Hello.java create mode 100644 procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/package-info.java create mode 100644 procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java create mode 100644 procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java create mode 100644 procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Procedure.java create mode 100644 procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoader.java create mode 100644 procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoadingTest.java diff --git a/procedural/examples/foo-procedures/README.md b/procedural/examples/foo-procedures/README.md new file mode 100644 index 0000000000..cfbac1442a --- /dev/null +++ b/procedural/examples/foo-procedures/README.md @@ -0,0 +1,11 @@ +# foo-procedures + +This is an example project for using procedural-based constraints and scheduling procedures. + +The main utility this subproject provides is the following gradle tasks, which provide automation for compiling and packaging large numbers of procedure / constraint jars, which will be uploaded to the Aerie system down the line. + +## `generateConstraintJarTasks` + +Iterates over all source files in + +## `buildAllConstraintJars` diff --git a/procedural/examples/foo-procedures/build.gradle b/procedural/examples/foo-procedures/build.gradle new file mode 100644 index 0000000000..eedf525530 --- /dev/null +++ b/procedural/examples/foo-procedures/build.gradle @@ -0,0 +1,98 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + id 'java' + id 'io.github.goooler.shadow' version '8.1.7' +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +repositories { + mavenCentral() +} + +dependencies { + annotationProcessor project(':procedural:processor') + implementation project(':procedural:constraints') + implementation project(':procedural:scheduling') + implementation project(':procedural:timeline') + implementation project(':merlin-framework') + implementation project(':merlin-driver') + implementation project(':contrib') + + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' +} + +tasks.register('buildAllSchedulingProcedureJars') { + group = 'SchedulingProcedureJars' + + dependsOn "generateSchedulingProcedureJarTasks" + dependsOn { + tasks.findAll { task -> task.name.startsWith('buildSchedulingProcedureJar_') } + } +} + +tasks.create("generateSchedulingProcedureJarTasks") { + group = 'SchedulingProcedureJars' + + final proceduresDir = findFirstMatchingBuildDir("generated/procedures") + + if (proceduresDir == null) { + println "No procedures folder found" + return + } + println "Generating jar tasks for the following procedures directory: ${proceduresDir}" + + final files = file(proceduresDir).listFiles() + if (files.length == 0) { + println "No procedures available within folder ${proceduresDir}" + return + } + + files.toList().each { file -> + final nameWithoutExtension = file.name.replace(".java", "") + final taskName = "buildSchedulingProcedureJar_${nameWithoutExtension}" + + println "Generating ${taskName} task, which will build ${nameWithoutExtension}.jar" + + tasks.create(taskName, ShadowJar) { + group = 'SchedulingProcedureJars' + configurations = [project.configurations.compileClasspath] + from sourceSets.main.output + archiveBaseName = "" // clear + archiveClassifier.set(nameWithoutExtension) // set output jar name + manifest { + attributes 'Main-Class': getMainClassFromGeneratedFile(file) + } + minimize() + dependencies { + // exclude project(':procedural:timeline') + // exclude dependency(":kotlin.*") + } + } + } +} + +private String findFirstMatchingBuildDir(String pattern) { + String found = null + final generatedDir = file("build/generated/sources") + generatedDir.mkdirs() + generatedDir.eachDirRecurse { dir -> if (dir.path.contains(pattern)) found = dir.path } + return found +} + +private static String getMainClassFromGeneratedFile(File file) { + final fileString = file.toString() + final prefix = "build/generated/sources/annotationProcessor/java/main/" + final index = fileString.indexOf(prefix) + prefix.length() + final trimmed = fileString.substring(index).replace(".java", "") + return trimmed.replace("/", ".") +} + +test { + useJUnitPlatform() +} diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/Helper.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/Helper.java new file mode 100644 index 0000000000..0308d22dd5 --- /dev/null +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/Helper.java @@ -0,0 +1,7 @@ +package gov.nasa.ammos.aerie.procedural.examples.fooprocedures; + +public class Helper { + public static String greeting() { + return "hello from util"; + } +} diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java new file mode 100644 index 0000000000..c934c367bd --- /dev/null +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java @@ -0,0 +1,21 @@ +package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.constraints; + +import gov.nasa.ammos.aerie.procedural.constraints.Violations; +import gov.nasa.ammos.aerie.procedural.constraints.Constraint; +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real; +import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions; +import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulatedPlan; +import org.jetbrains.annotations.NotNull; + +public class ConstFruit implements Constraint { + @NotNull + @Override + public Violations run(SimulatedPlan plan, @NotNull CollectOptions options) { + final var fruit = plan.resource("/fruit", Real::deserialize); + + return Violations.violateOn( + fruit.equalTo(4), + false + ); + } +} diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Hello.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Hello.java new file mode 100644 index 0000000000..1e36a1d2c1 --- /dev/null +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Hello.java @@ -0,0 +1,9 @@ +package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.constraints; + +import gov.nasa.ammos.aerie.procedural.examples.fooprocedures.Helper; + +class Hello { + public static void main(String[] args) { + System.out.println(Helper.greeting()); + } +} diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/package-info.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/package-info.java new file mode 100644 index 0000000000..ab7662c4d2 --- /dev/null +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/package-info.java @@ -0,0 +1,5 @@ +@WithMappers(BasicValueMappers.class) +package gov.nasa.ammos.aerie.procedural.examples.fooprocedures; + +import gov.nasa.jpl.aerie.contrib.serialization.rulesets.BasicValueMappers; +import gov.nasa.ammos.aerie.procedural.scheduling.annotations.WithMappers; diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java new file mode 100644 index 0000000000..6163a7eec2 --- /dev/null +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java @@ -0,0 +1,31 @@ +package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.procedures; + +import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan; +import gov.nasa.ammos.aerie.procedural.scheduling.Rule; +import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure; +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart; + +import java.util.Map; + +@SchedulingProcedure +public record SampleProcedure(int quantity) implements Rule { + @Override + public void run(EditablePlan plan) { + final var firstTime = Duration.hours(24); + final var step = Duration.hours(6); + + var currentTime = firstTime; + for (var i = 0; i < quantity; i++) { + plan.create( + "BiteBanana", + new DirectiveStart.Absolute(currentTime), + Map.of() + ); + currentTime = currentTime.plus(step); + } + plan.commit(); +// var results = plan.simulate(new SimulateOptions()); +// var size = results.instances().collect().size(); + } +} diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java new file mode 100644 index 0000000000..6e79616ee8 --- /dev/null +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java @@ -0,0 +1,62 @@ +package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.procedures; + +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; +import gov.nasa.ammos.aerie.procedural.scheduling.Rule; +import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure; +import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan; +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart; + +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; +import java.util.Map; + +@SchedulingProcedure +public record SimulationDemo(int quantity) implements Rule { + @Override + public void run(EditablePlan plan) { +// final var firstActivityTime = plan.toRelative(Instant.from(DOY_WITHOUT_ZONE_FORMATTER.parse("2024-128T07:00:00"))); +// +// plan.create( +// "BiteBanana", +// new DirectiveStart.Absolute(firstActivityTime), +// Map.of("biteSize", SerializedValue.of(2)) +// ); + + var simResults = plan.latestResults(); + if (simResults == null) simResults = plan.simulate(); + + final var lowFruit = simResults.resource("/fruit", Real::deserialize).lessThan(3.5).isolateTrue(); + final var bites = simResults.instances("BiteBanana"); + + final var connections = lowFruit.starts().shift(Duration.MINUTE.negate()) + .connectTo(bites.ends(), false); + + for (final var connection: connections.collect()) { + assert connection.to != null; + plan.create( + "GrowBanana", + new DirectiveStart.Anchor( + connection.to.directiveId, + Duration.minutes(30), + DirectiveStart.Anchor.AnchorPoint.End + ), + Map.of( + "quantity", SerializedValue.of(1), + "growingDuration", SerializedValue.of(Duration.HOUR.dividedBy(Duration.MICROSECOND)) + ) + ); + } + + plan.commit(); + } + + private static final DateTimeFormatter DOY_WITHOUT_ZONE_FORMATTER = new DateTimeFormatterBuilder() + .appendPattern("uuuu-DDD'T'HH:mm:ss") + .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true) + .toFormatter() + .withZone(ZoneOffset.UTC); +} diff --git a/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Procedure.java b/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Procedure.java new file mode 100644 index 0000000000..cd1cd5927d --- /dev/null +++ b/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Procedure.java @@ -0,0 +1,4 @@ +package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.constraints; + +public interface Procedure { +} diff --git a/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoader.java b/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoader.java new file mode 100644 index 0000000000..4a60866c2b --- /dev/null +++ b/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoader.java @@ -0,0 +1,68 @@ +package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.constraints; + +import gov.nasa.jpl.aerie.merlin.protocol.model.MerlinPlugin; +import gov.nasa.ammos.aerie.procedural.constraints.Constraint; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.Objects; +import java.util.jar.JarFile; + +public final class ProcedureLoader { + public static Constraint loadProcedure(final Path path, final String name, final String version) + throws ProcedureLoadException + { + final var className = getImplementingClassName(path, name, version); + final var classLoader = new URLClassLoader(new URL[] {pathToUrl(path)}); + + try { + final var pluginClass$ = classLoader.loadClass(className); + if (!Constraint.class.isAssignableFrom(pluginClass$)) { + throw new ProcedureLoadException(path, name, version); + } + + return (Constraint) pluginClass$.getConstructor().newInstance(); + } catch (final ReflectiveOperationException ex) { + throw new ProcedureLoadException(path, name, version, ex); + } + } + + private static String getImplementingClassName(final Path jarPath, final String name, final String version) + throws ProcedureLoadException { + try (final var jarFile = new JarFile(jarPath.toFile())) { + return Objects.requireNonNull(jarFile.getManifest().getMainAttributes().getValue("Main-Class")); + } catch (final IOException ex) { + throw new ProcedureLoadException(jarPath, name, version, ex); + } + } + + private static URL pathToUrl(final Path path) { + try { + return path.toUri().toURL(); + } catch (final MalformedURLException ex) { + // This exception only happens if there is no URL protocol handler available to represent a Path. + // This is highly unexpected, and indicates a fundamental problem with the system environment. + throw new Error(ex); + } + } + + public static class ProcedureLoadException extends Exception { + private ProcedureLoadException(final Path path, final String name, final String version) { + this(path, name, version, null); + } + + private ProcedureLoadException(final Path path, final String name, final String version, final Throwable cause) { + super( + String.format( + "No implementation found for `%s` at path `%s` wih name \"%s\" and version \"%s\"", + MerlinPlugin.class.getSimpleName(), + path, + name, + version), + cause); + } + } +} diff --git a/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoadingTest.java b/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoadingTest.java new file mode 100644 index 0000000000..7db3ebfb05 --- /dev/null +++ b/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoadingTest.java @@ -0,0 +1,16 @@ +package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.constraints; + +import gov.nasa.ammos.aerie.procedural.constraints.Constraint; + +import java.nio.file.Path; + +public class ProcedureLoadingTest { + void foo() throws ProcedureLoader.ProcedureLoadException { + var jarPath = "/Users/dailis/projects/aerie/worktrees/develop/procedural/examples/foo-procedures/build/libs/foo-procedures-ConstFruit-constraint.jar"; + + // Load jar from absolute filepath + final Constraint constraint = ProcedureLoader.loadProcedure(Path.of(jarPath), "name", "version"); + // Run code + + } +} diff --git a/settings.gradle b/settings.gradle index cbb1a2575b..1240589c6b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,6 +19,9 @@ include 'procedural:constraints' include 'procedural:scheduling' include 'procedural:remote' +// Procedural examples +include 'procedural:examples:foo-procedures' + // Services for deployment within the Aerie infrastructure include 'merlin-server' include 'merlin-worker' From 3abc3fdd2279ed2ac53077c494f112e112fddd26 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Tue, 20 Aug 2024 11:37:18 -0700 Subject: [PATCH 028/108] Build procedure jars in CI --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 82a0640073..45697a5882 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -77,6 +77,8 @@ jobs: uses: gradle/actions/setup-gradle@v3 - name: Assemble run: ./gradlew assemble --parallel + - name: Build scheduling procedure jars for testing + run: ./gradlew procedural:examples:foo-procedures:buildAllSchedulingProcedureJars - name: Start Services run: | docker compose -f ./e2e-tests/docker-compose-test.yml up -d --build From 22b1ef18bd3588865f5fb5ac3c0078df3fa2d3f7 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Tue, 20 Aug 2024 11:37:23 -0700 Subject: [PATCH 029/108] Update e2e tests --- .../nasa/jpl/aerie/e2e/SchedulingTests.java | 135 +++++++++++++++--- .../jpl/aerie/e2e/TimelineRemoteTests.java | 5 +- .../jpl/aerie/e2e/types/GoalInvocationId.java | 3 + .../nasa/jpl/aerie/e2e/types/GoalType.java | 6 + .../gov/nasa/jpl/aerie/e2e/utils/GQL.java | 25 +++- .../jpl/aerie/e2e/utils/HasuraRequests.java | 73 ++++++++-- 6 files changed, 208 insertions(+), 39 deletions(-) create mode 100644 e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalInvocationId.java create mode 100644 e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalType.java diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java index 4b2b45de67..fa7fef6b45 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java @@ -3,6 +3,7 @@ import com.microsoft.playwright.Playwright; import gov.nasa.jpl.aerie.e2e.types.ExternalDataset.ProfileInput; import gov.nasa.jpl.aerie.e2e.types.ExternalDataset.ProfileInput.ProfileSegmentInput; +import gov.nasa.jpl.aerie.e2e.types.GoalInvocationId; import gov.nasa.jpl.aerie.e2e.types.Plan; import gov.nasa.jpl.aerie.e2e.types.ProfileSegment; import gov.nasa.jpl.aerie.e2e.types.SchedulingRequest.SchedulingStatus; @@ -31,6 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -188,7 +190,7 @@ void twoInARow() throws IOException { "BakeBanana Scheduling Test Goal", bakeBananaGoalDefinition, schedulingSpecId, - 0); + 0).goalId(); try { // Schedule and get Plan hasura.awaitScheduling(schedulingSpecId); @@ -219,7 +221,7 @@ void schedulingRecurrenceGoal() throws IOException { "Recurrence Scheduling Test Goal", recurrenceGoalDefinition, schedulingSpecId, - 0); + 0).goalId(); try { // Schedule and get Plan hasura.awaitScheduling(schedulingSpecId, 100000000); @@ -243,7 +245,7 @@ void schedulingCoexistenceGoal() throws IOException { "Coexistence Scheduling Test Goal", coexistenceGoalDefinition, schedulingSpecId, - 0); + 0).goalId(); try { // Schedule and get Plan @@ -314,12 +316,12 @@ void schedulingMultipleGoals() throws IOException { "Recurrence Scheduling Test Goal", recurrenceGoalDefinition, schedulingSpecId, - 0); + 0).goalId(); final int coexistenceGoalId = hasura.createSchedulingSpecGoal( "Coexistence Scheduling Test Goal", coexistenceGoalDefinition, schedulingSpecId, - 1); + 1).goalId(); try { // Schedule and get Plan hasura.awaitScheduling(schedulingSpecId); @@ -373,7 +375,7 @@ void schedulingGoalPostsSimResults() throws IOException { "Coexistence Scheduling Test Goal", coexistenceGoalDefinition, schedulingSpecId, - 0); + 0).goalId(); try { // Schedule and get Plan @@ -469,12 +471,12 @@ void schedulingDoesNotPostSimResultsWithSimulation() throws IOException { "Coexistence Scheduling Test Goal", coexistenceGoalDefinition, schedulingSpecId, - 0); + 0).goalId(); try{ final var response = hasura.awaitSimulation(planId); final var simDataset = hasura.getSimulationDataset(response.simDatasetId()); final var schedulingResults = hasura.awaitScheduling(schedulingSpecId); - assertEquals(schedulingResults.datasetId(), simDataset.datasetId()); + assertEquals(simDataset.datasetId(), schedulingResults.datasetId()); } finally { // Teardown: Delete Goal hasura.deleteSchedulingGoal(coexistenceGoalId); @@ -505,7 +507,7 @@ void outdatedPlanRevision() throws IOException { "Coexistence Scheduling Test Goal", coexistenceGoalDefinition, schedulingSpecId, - 0); + 0).goalId(); try { hasura.updatePlanRevisionSchedulingSpec(planId); @@ -556,7 +558,7 @@ void outdatedSimConfig() throws IOException { "Scheduling Test: When Plant < 300", plantCountGoalDefinition, schedulingSpecId, - 0); + 0).goalId(); try { hasura.awaitScheduling(schedulingSpecId); @@ -603,7 +605,7 @@ void injectedResultsLoaded() throws IOException{ "Scheduling Test: When Plant < 300", plantCountGoalDefinition, schedulingSpecId, - 0); + 0).goalId(); try { hasura.awaitScheduling(schedulingSpecId); @@ -631,7 +633,7 @@ void temporalSubsetExcluded() throws IOException { "Coexistence Scheduling Test Goal", coexistenceGoalDefinition, schedulingSpecId, - 0); + 0).goalId(); try { // Schedule and get Plan @@ -686,7 +688,7 @@ export default function cardinalityGoalExample() { specification: { duration: Temporal.Duration.from({ seconds: 10 }) }, });}""", schedulingSpecId, - 0); + 0).goalId(); } @AfterEach @@ -776,7 +778,7 @@ export default function myGoal() { }) }""", schedulingSpecId, - 0); + 0).goalId(); } @AfterEach @@ -883,7 +885,7 @@ export default function recurrenceGoalExample() { }); }""", fooSchedulingSpecId, - 0); + 0).goalId(); } @AfterEach @@ -924,15 +926,17 @@ void cancelingSchedulingUpdatesRequestReason() throws IOException { class VersioningSchedulingGoals { @Test void goalVersionLocking() throws IOException { - final int goalId = hasura.createSchedulingSpecGoal( + final var goalInvocationId = hasura.createSchedulingSpecGoal( "coexistence goal", coexistenceGoalDefinition, schedulingSpecId, 0); + final var goalId = goalInvocationId.goalId(); + final var invocationId = goalInvocationId.invocationId(); try { // Update the plan's constraint specification to use a specific version - hasura.updateSchedulingSpecVersion(schedulingSpecId, goalId, 0); + hasura.updateSchedulingSpecVersion(invocationId, 0); // Update definition to have invalid syntax final int newRevision = hasura.updateGoalDefinition( @@ -944,7 +948,7 @@ void goalVersionLocking() throws IOException { assertEquals("complete", initResults.status()); // Update scheduling spec to use invalid definition - hasura.updateSchedulingSpecVersion(schedulingSpecId, goalId, newRevision); + hasura.updateSchedulingSpecVersion(invocationId, newRevision); // Schedule -- should fail final var error = Assertions.assertThrows( @@ -964,21 +968,23 @@ void goalVersionLocking() throws IOException { @Test void schedulingIgnoreDisabledGoals() throws IOException { // Add a problematic goal to the spec, then disable it - final int problemGoalId = hasura.createSchedulingSpecGoal( + final var problemGoalInvocationId = hasura.createSchedulingSpecGoal( "bad goal", "error :-(", "Goal that won't compile", schedulingSpecId, 0); + final var problemGoalId = problemGoalInvocationId.goalId(); + final var problemInvocationId = problemGoalInvocationId.invocationId(); try { - hasura.updateSchedulingSpecEnabled(schedulingSpecId, problemGoalId, false); + hasura.updateSchedulingSpecEnabled(problemInvocationId, false); // Schedule -- Validate that the plan didn't change hasura.awaitScheduling(schedulingSpecId); assertEquals(0, hasura.getPlan(planId).activityDirectives().size()); // Enable disabled constraint - hasura.updateSchedulingSpecEnabled(schedulingSpecId, problemGoalId, true); + hasura.updateSchedulingSpecEnabled(problemInvocationId, true); // Schedule -- Assert Fail final var error = Assertions.assertThrows( @@ -995,4 +1001,91 @@ void schedulingIgnoreDisabledGoals() throws IOException { } } } + + @Nested + class ProceduralSchedulingTests { + private int modelId; + private int planId; + private int specId; + private int procedureJarId; + private GoalInvocationId procedureId; + + @BeforeEach + void beforeEach() throws IOException, InterruptedException { + try (final var gateway = new GatewayRequests(playwright)) { + modelId = hasura.createMissionModel( + gateway.uploadJarFile(), + "Banananation (e2e tests)", + "aerie_e2e_tests", + "Proc Scheduling Tests"); + + procedureJarId = gateway.uploadJarFile("../procedural/examples/foo-procedures/build/libs/SampleProcedure.jar"); + } + // Insert the Plan + planId = hasura.createPlan( + modelId, + "Proc Sched Plan - Proc Scheduling Tests", + "48:00:00", + planStartTimestamp); + specId = hasura.getSchedulingSpecId(planId); + + // Add Scheduling Procedure + procedureId = hasura.createSchedulingSpecProcedure( + "Test Scheduling Procedure", + procedureJarId, + specId, + 0); + } + + @AfterEach + void afterEach() throws IOException { + hasura.deleteSchedulingGoal(procedureId.goalId()); + hasura.deletePlan(planId); + hasura.deleteMissionModel(modelId); + } + + /** + * Upload a procedure jar and add to spec + */ + @Test + void proceduralUploadWorks() throws IOException { + final var ids = hasura.getSchedulingSpecGoalIds(specId); + + assertEquals(1, ids.size()); + assertEquals(procedureId.goalId(), ids.getFirst()); + } + + /** + * Run a spec with one procedure in it with required params but no args set + * Should fail scheduling run + */ + @Test + void executeSchedulingRunWithoutArguments() throws IOException { + assertThrows(AssertionFailedError.class, () -> hasura.awaitScheduling(specId)); + } + + /** + * Run a spec with one procedure in it + */ + @Test + void executeSchedulingRunWithArguments() throws IOException { + final var args = Json.createObjectBuilder().add("quantity", 2).build(); + + hasura.updateSchedulingSpecGoalArguments(procedureId.invocationId(), args); + + final var resp = hasura.awaitScheduling(specId); + + final var plan = hasura.getPlan(planId); + final var activities = plan.activityDirectives(); + + assertEquals(2, activities.size()); + final var first = activities.getFirst(); + assertEquals(first.type(), "BiteBanana"); + assertEquals(first.startOffset(), "24:00:00"); + + final var second = activities.getLast(); + assertEquals(second.type(), "BiteBanana"); + assertEquals(second.startOffset(), "30:00:00"); + } + } } diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java index 20faa10256..75713a8d38 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java @@ -118,7 +118,8 @@ void queryActivityInstances() { assertEquals(1, instances.size()); final var instance = instances.get(0); assertEquals("BiteBanana", instance.getType()); - assertEquals(activityId, instance.directiveId); + assert instance.directiveId != null; + assertEquals(activityId, instance.directiveId.id()); assertEquals(1, instance.inner.arguments.get("biteSize").asInt().get()); assertEquals(Duration.ZERO, instance.getInterval().duration()); } @@ -129,7 +130,7 @@ void queryActivityDirectives() { assertEquals(1, directives.size()); final var directive = directives.get(0); assertEquals("BiteBanana", directive.getType()); - assertEquals(activityId, directive.id); + assertEquals(activityId, directive.id.id()); assertEquals(1, directive.inner.arguments.get("biteSize").asInt().get()); } diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalInvocationId.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalInvocationId.java new file mode 100644 index 0000000000..07acdc4cf6 --- /dev/null +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalInvocationId.java @@ -0,0 +1,3 @@ +package gov.nasa.jpl.aerie.e2e.types; + +public record GoalInvocationId(int goalId, int invocationId) { } diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalType.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalType.java new file mode 100644 index 0000000000..daabae20e9 --- /dev/null +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalType.java @@ -0,0 +1,6 @@ +package gov.nasa.jpl.aerie.e2e.types; + +public enum GoalType { + JAR, + EDSL +} diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java index 0a53c0ef67..8a5eed0217 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java @@ -99,6 +99,7 @@ mutation CreatePlan($plan: plan_insert_input!) { mutation CreateSchedulingSpecGoal($spec_goal: scheduling_specification_goals_insert_input!) { insert_scheduling_specification_goals_one(object: $spec_goal) { goal_id + goal_invocation_id priority specification_id } @@ -419,6 +420,12 @@ query GetSchedulingSpec($planId: Int!) { id } }"""), + GET_SCHEDULING_SPECIFICATION_GOALS(""" + query GetSchedulingSpecGoals($specId: Int!) { + goals: scheduling_specification_goals(where: {specification_id: {_eq: $specId}}) { + goal_id + } + }"""), GET_SIMULATION_CONFIGURATION(""" query GetSimConfig($planId: Int!) { sim_config: simulation(where: {plan_id: {_eq:$planId}}) { @@ -607,10 +614,20 @@ mutation updateRolePermissions($role: user_roles_enum!, $action_permissions: jso action_permissions } }"""), + UPDATE_SCHEDULING_SPEC_GOALS_ARGUMENTS(""" + mutation updateSchedulingSpecGoalArguments($goal_invocation_id: Int!, $arguments: jsonb!) { + update_scheduling_specification_goals_by_pk( + pk_columns: {goal_invocation_id: $goal_invocation_id}, + _set: {arguments: $arguments}) + { + goal_revision + arguments + } + }"""), UPDATE_SCHEDULING_SPEC_GOALS_ENABLED(""" - mutation updateSchedulingSpecGoalVersion($spec_id: Int!, $goal_id: Int!, $enabled: Boolean!) { + mutation updateSchedulingSpecGoalVersion($goal_invocation_id: Int!, $enabled: Boolean!) { update_scheduling_specification_goals_by_pk( - pk_columns: {specification_id: $spec_id, goal_id: $goal_id}, + pk_columns: {goal_invocation_id: $goal_invocation_id}, _set: {enabled: $enabled}) { goal_revision @@ -618,9 +635,9 @@ mutation updateSchedulingSpecGoalVersion($spec_id: Int!, $goal_id: Int!, $enable } }"""), UPDATE_SCHEDULING_SPEC_GOALS_VERSION(""" - mutation updateSchedulingSpecGoalVersion($spec_id: Int!, $goal_id: Int!, $goal_revision: Int!) { + mutation updateSchedulingSpecGoalVersion($goal_invocation_id: Int!, $goal_revision: Int!) { update_scheduling_specification_goals_by_pk( - pk_columns: {specification_id: $spec_id, goal_id: $goal_id}, + pk_columns: {goal_invocation_id: $goal_invocation_id}, _set: {goal_revision: $goal_revision}) { goal_revision diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java index aced50855d..9f7df289d0 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java @@ -17,6 +17,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.OptionalLong; import java.util.function.Function; import java.util.stream.Collectors; @@ -624,7 +626,9 @@ public SchedulingRequest cancelingScheduling(int schedulingSpecId, int timeout) } public void deleteSchedulingGoal(int goalId) throws IOException { - final var variables = Json.createObjectBuilder().add("goalId", goalId).build(); + final var variables = Json.createObjectBuilder() + .add("goalId", goalId) + .build(); makeRequest(GQL.DELETE_SCHEDULING_GOAL, variables); } @@ -635,6 +639,13 @@ public int getSchedulingSpecId(int planId) throws IOException { return spec.getJsonObject(0).getInt("id"); } + public List getSchedulingSpecGoalIds(int specId) throws IOException { + final var vars = Json.createObjectBuilder().add("specId", specId).build(); + final var goals = makeRequest(GQL.GET_SCHEDULING_SPECIFICATION_GOALS, vars).getJsonArray("goals"); + + return goals.stream().map(e -> e.asJsonObject().getInt("goal_id")).toList(); + } + public void updatePlanRevisionSchedulingSpec(int planId) throws IOException { final var variables = Json.createObjectBuilder() .add("planId", planId) @@ -643,7 +654,38 @@ public void updatePlanRevisionSchedulingSpec(int planId) throws IOException { makeRequest(GQL.UPDATE_SCHEDULING_SPECIFICATION_PLAN_REVISION, variables); } - public int createSchedulingSpecGoal( + + public GoalInvocationId createSchedulingSpecProcedure( + String name, + int jarId, + int specificationId, + int priority + ) throws IOException { + final var specGoalBuilder = Json.createObjectBuilder() + .add("goal_metadata", + Json.createObjectBuilder() + .add("data", + Json.createObjectBuilder() + .add("name", name) + .add("description", "") + .add("versions", + Json.createObjectBuilder() + .add("data", + Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "JAR") + .add("uploaded_jar_id", jarId) + ))))) + .add("specification_id", specificationId) + .add("priority", priority); + final var variables = Json.createObjectBuilder().add("spec_goal", specGoalBuilder).build(); + final var resp = makeRequest(GQL.CREATE_SCHEDULING_SPEC_GOAL, variables) + .getJsonObject("insert_scheduling_specification_goals_one"); + + return new GoalInvocationId(resp.getInt("goal_id"), resp.getInt("goal_invocation_id")); + } + + public GoalInvocationId createSchedulingSpecGoal( String name, String definition, int specificationId, @@ -652,7 +694,7 @@ public int createSchedulingSpecGoal( return createSchedulingSpecGoal(name, definition, "", specificationId, priority); } - public int createSchedulingSpecGoal( + public GoalInvocationId createSchedulingSpecGoal( String name, String definition, String description, @@ -675,9 +717,10 @@ public int createSchedulingSpecGoal( .add("specification_id", specificationId) .add("priority", priority); final var variables = Json.createObjectBuilder().add("spec_goal", specGoalBuilder).build(); - return makeRequest(GQL.CREATE_SCHEDULING_SPEC_GOAL, variables) - .getJsonObject("insert_scheduling_specification_goals_one") - .getInt("goal_id"); + final var resp = makeRequest(GQL.CREATE_SCHEDULING_SPEC_GOAL, variables) + .getJsonObject("insert_scheduling_specification_goals_one"); + + return new GoalInvocationId(resp.getInt("goal_id"), resp.getInt("goal_invocation_id")); } public int updateGoalDefinition(int goalId, String definition) throws IOException { @@ -688,19 +731,25 @@ public int updateGoalDefinition(int goalId, String definition) throws IOExceptio return makeRequest(GQL.UPDATE_GOAL_DEFINITION, variables).getJsonObject("definition").getInt("revision"); } - public void updateSchedulingSpecEnabled(int schedulingSpecId, int goalId, boolean enabled) throws IOException { + public void updateSchedulingSpecGoalArguments(int invocationId, JsonObject arguments) throws IOException { final var variables = Json.createObjectBuilder() - .add("spec_id", schedulingSpecId) - .add("goal_id", goalId) + .add("goal_invocation_id", invocationId) + .add("arguments", arguments) + .build(); + makeRequest(GQL.UPDATE_SCHEDULING_SPEC_GOALS_ARGUMENTS, variables); + } + + public void updateSchedulingSpecEnabled(int invocationId, boolean enabled) throws IOException { + final var variables = Json.createObjectBuilder() + .add("goal_invocation_id", invocationId) .add("enabled", enabled) .build(); makeRequest(GQL.UPDATE_SCHEDULING_SPEC_GOALS_ENABLED, variables); } - public void updateSchedulingSpecVersion(int schedulingSpecId, int goalId, int version) throws IOException { + public void updateSchedulingSpecVersion(int invocationId, int version) throws IOException { final var variables = Json.createObjectBuilder() - .add("spec_id", schedulingSpecId) - .add("goal_id", goalId) + .add("goal_invocation_id", invocationId) .add("goal_revision", version) .build(); makeRequest(GQL.UPDATE_SCHEDULING_SPEC_GOALS_VERSION, variables); From ddb5692ab2ef3753172544de5dd0d4e740cdb62a Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Tue, 20 Aug 2024 11:37:27 -0700 Subject: [PATCH 030/108] add simple db test for goal / procedure type DB constraint check --- .../database/SchedulerDatabaseTests.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/db-tests/src/test/java/gov/nasa/jpl/aerie/database/SchedulerDatabaseTests.java b/db-tests/src/test/java/gov/nasa/jpl/aerie/database/SchedulerDatabaseTests.java index 2ea0095c21..fc16d71b83 100644 --- a/db-tests/src/test/java/gov/nasa/jpl/aerie/database/SchedulerDatabaseTests.java +++ b/db-tests/src/test/java/gov/nasa/jpl/aerie/database/SchedulerDatabaseTests.java @@ -1,6 +1,7 @@ package gov.nasa.jpl.aerie.database; import org.junit.jupiter.api.*; +import org.postgresql.util.PSQLException; import java.io.IOException; import java.sql.Connection; @@ -99,6 +100,28 @@ with metadata(id, owner) as ( } } + int insertSchedulingProcedure() throws SQLException { + var jarId = merlinHelper.insertFileUpload(); + + try (final var statement = connection.createStatement()) { + final var res = statement.executeQuery( + //language=sql + """ + with metadata(id, owner) as ( + insert into scheduler.scheduling_goal_metadata(name, description, owner, updated_by) + values ('test procedure', 'no-op', 'scheduler db tests', 'scheduler db tests') + returning id, owner + ) + insert into scheduler.scheduling_goal_definition(goal_id, uploaded_jar_id, type, author) + select m.id, %d, 'JAR', m.owner + from metadata m + returning goal_id as id; + """.formatted(jarId)); + res.next(); + return res.getInt("id"); + } + } + void addGoalToModelSpec(int modelId, int goalId, int priority) throws SQLException { try (final var statement = connection.createStatement()) { statement.executeUpdate( @@ -530,4 +553,64 @@ void shouldGeneratePriorityWhenNull() throws SQLException { } } } + + @Nested + class ProceduralSchedulingTests { + int specId; + int[] goalIds; + @BeforeEach + void beforeEach() throws SQLException { + final int modelId = merlinHelper.insertMissionModel(merlinHelper.insertFileUpload()); + specId = getSpecificationId(merlinHelper.insertPlan(modelId)); + goalIds = new int[]{insertGoal(), insertSchedulingProcedure()}; + } + + @AfterEach + void afterEach() throws SQLException { + helper.clearSchema("merlin"); + helper.clearSchema("scheduler"); + } + + @Test + void testCantPartiallyChangeProcedureToEDSLGoal() throws SQLException { + try (final var statement = connection.createStatement()) { + final var exception = assertThrowsExactly( + PSQLException.class, + () -> statement.executeUpdate( + //language=sql + """ + update scheduler.scheduling_goal_definition + set type = 'EDSL' + where goal_id = %d + """.formatted(goalIds[1]) + ) + ); + + assertTrue(exception.getMessage().contains( + "new row for relation \"scheduling_goal_definition\" violates check constraint \"check_goal_definition_type_consistency\"") + ); + } + } + + @Test + void testCantPartiallyChangeEDSLGoalToProcedure() throws SQLException { + try (final var statement = connection.createStatement()) { + final var exception = assertThrowsExactly( + PSQLException.class, + () -> statement.executeUpdate( + //language=sql + """ + update scheduler.scheduling_goal_definition + set type = 'JAR' + where goal_id = %d + """.formatted(goalIds[0]) + ) + ); + + assertTrue(exception.getMessage().contains( + "new row for relation \"scheduling_goal_definition\" violates check constraint \"check_goal_definition_type_consistency\"") + ); + } + } + } } From 6d881b9e4467a70aaaede51a3b6141edec094f40 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 14 Aug 2024 15:40:33 -0700 Subject: [PATCH 031/108] Create interval step function --- .../aerie/procedural/timeline/Interval.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Interval.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Interval.kt index b286c92923..b8cb6e8e4d 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Interval.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Interval.kt @@ -280,6 +280,29 @@ data class Interval @JvmOverloads constructor( return listOf(left, right).filterNot { it.isEmpty() } } + /** + * Creates an iterable that produces durations spread out over the interval, separated by `stride`. + * + * Respects inclusivity; if the start or end is not included then it will not be produced by the iterator. + * + * If the length of the interval is not a multiple of the stride, the endpoint will not be produced. + * + * @return an iterable over durations in the interval separated by `stride` + */ + infix fun step(stride: Duration) = object : Iterable { + override fun iterator() = object : Iterator { + var nextTime = if (includesStart()) start else start + stride + + override fun hasNext() = contains(nextTime) + + override fun next(): Duration { + val result = if (hasNext()) nextTime else throw NoSuchElementException() + nextTime += stride + return result + } + } + } + /***/ override fun toString(): String { return if (isEmpty()) { From 313fce9f3511126a9387ed487b1a12fc1a77b5c4 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 22 Aug 2024 17:04:19 -0700 Subject: [PATCH 032/108] Update to kotlin 2.0 --- procedural/build.gradle | 2 +- procedural/constraints/build.gradle | 2 +- procedural/remote/build.gradle | 2 +- procedural/scheduling/build.gradle | 2 +- procedural/timeline/build.gradle | 2 +- .../nasa/ammos/aerie/procedural/timeline/payloads/Segment.kt | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/procedural/build.gradle b/procedural/build.gradle index b7c8dfd9ce..a1f34d94cc 100644 --- a/procedural/build.gradle +++ b/procedural/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.9.22" + id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'org.jetbrains.dokka' version '1.9.10' } diff --git a/procedural/constraints/build.gradle b/procedural/constraints/build.gradle index f425e980fc..87e8f93561 100644 --- a/procedural/constraints/build.gradle +++ b/procedural/constraints/build.gradle @@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { id 'com.github.johnrengelman.shadow' version '8.1.1' - id "org.jetbrains.kotlin.jvm" version "1.9.22" + id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' } diff --git a/procedural/remote/build.gradle b/procedural/remote/build.gradle index 73c1f742af..d54145aea9 100644 --- a/procedural/remote/build.gradle +++ b/procedural/remote/build.gradle @@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { id 'com.github.johnrengelman.shadow' version '8.1.1' - id "org.jetbrains.kotlin.jvm" version "1.9.22" + id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' } diff --git a/procedural/scheduling/build.gradle b/procedural/scheduling/build.gradle index 08d4ea15b3..de5cef2745 100644 --- a/procedural/scheduling/build.gradle +++ b/procedural/scheduling/build.gradle @@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { id 'com.github.johnrengelman.shadow' version '8.1.1' - id "org.jetbrains.kotlin.jvm" version "1.9.22" + id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' id 'maven-publish' diff --git a/procedural/timeline/build.gradle b/procedural/timeline/build.gradle index d7329c3a4e..934b797369 100644 --- a/procedural/timeline/build.gradle +++ b/procedural/timeline/build.gradle @@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { id 'com.github.johnrengelman.shadow' version '8.1.1' - id "org.jetbrains.kotlin.jvm" version "1.9.22" + id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' id 'maven-publish' diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/Segment.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/Segment.kt index 40f0bba81e..3688dff396 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/Segment.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/Segment.kt @@ -14,7 +14,7 @@ data class Segment(/***/ override val interval: Interval, /***/ @JvmField val * * @param f a function that takes `this` as argument and produces a new value. */ - fun mapValue(f: (Segment) -> W) = Segment(interval, f(this)) + inline fun mapValue(f: (Segment) -> W) = Segment(interval, f(this)) /** * Create a new segment with the same value, on a new interval derived from this segment. @@ -23,7 +23,7 @@ data class Segment(/***/ override val interval: Interval, /***/ @JvmField val * * @param f a function that takes `this` as argument and produces a new interval. */ - fun mapInterval(f: (Segment) -> Interval) = Segment(f(this), value) + inline fun mapInterval(f: (Segment) -> Interval) = Segment(f(this), value) /** * Creates a new segment with the same value on a new interval. From 741836d7fb848548929fd9055d71ff1bc837d81f Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 22 Aug 2024 17:17:53 -0700 Subject: [PATCH 033/108] Fixes after rebase --- .../jpl/aerie/scheduler/goals/Procedure.java | 5 +- .../scheduler/solver/PrioritySolver.java | 172 +++++++++--------- 2 files changed, 89 insertions(+), 88 deletions(-) diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java index fe0b5ca0fa..fe5cc954b5 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java @@ -13,6 +13,7 @@ import gov.nasa.jpl.aerie.scheduler.plan.InMemoryEditablePlan; import gov.nasa.jpl.aerie.scheduler.plan.SchedulerToProcedurePlanAdapter; import gov.nasa.jpl.aerie.scheduler.simulation.SimulationFacade; +import gov.nasa.jpl.aerie.scheduler.solver.ConflictSatisfaction; import gov.nasa.jpl.aerie.scheduler.solver.Evaluation; import org.apache.commons.lang3.NotImplementedException; @@ -84,8 +85,8 @@ public void run(Evaluation eval, Plan plan, MissionModel missionModel, Functi final var evaluation = eval.forGoal(this); for (final var activity : newActivities) { plan.add(activity); - evaluation.associate(activity, true); + evaluation.associate(activity, true, null); } - evaluation.setScore(0.0); + evaluation.setConflictSatisfaction(null, ConflictSatisfaction.SAT); } } diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java index 412ce93ca2..7065abd55f 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java @@ -156,7 +156,7 @@ public PrioritySolver(final Problem problem, final boolean analysisOnly) { .max(Long::compareTo) .orElse(-1L) + 1 - ); + ); } public PrioritySolver(final Problem problem) { @@ -216,7 +216,7 @@ private InsertActivityResult checkAndInsertActs(Collection a var duration = act.duration(); if(duration != null && act.startOffset().plus(duration).longerThan(this.problem.getPlanningHorizon().getEndAerie())) { logger.warn("Not simulating activity " + act - + " because it is planned to finish after the end of the planning horizon."); + + " because it is planned to finish after the end of the planning horizon."); return new InsertActivityResult(allGood, List.of()); } } @@ -333,72 +333,72 @@ private void satisfyGoal(Goal goal) throws SchedulingInterruptedException{ } private void satisfyOptionGoal(OptionGoal goal) throws SchedulingInterruptedException{ - if (goal.hasOptimizer()) { - //try to satisfy all and see what is best - Goal currentSatisfiedGoal = null; - Collection actsToInsert = null; - Collection actsToAssociateWith = null; - for (var subgoal : goal.getSubgoals()) { - satisfyGoal(subgoal); - if(plan.getEvaluation().forGoal(subgoal).getScore() == 0 || !subgoal.shouldRollbackIfUnsatisfied()) { - var associatedActivities = plan.getEvaluation().forGoal(subgoal).getAssociatedActivities(); - var insertedActivities = plan.getEvaluation().forGoal(subgoal).getInsertedActivities(); - var aggregatedActivities = new ArrayList(); - aggregatedActivities.addAll(associatedActivities); - aggregatedActivities.addAll(insertedActivities); - if (!aggregatedActivities.isEmpty() && - (goal.getOptimizer().isBetterThanCurrent(aggregatedActivities) || - currentSatisfiedGoal == null)) { - actsToInsert = insertedActivities; - actsToAssociateWith = associatedActivities; - currentSatisfiedGoal = subgoal; - } + if (goal.hasOptimizer()) { + //try to satisfy all and see what is best + Goal currentSatisfiedGoal = null; + Collection actsToInsert = null; + Collection actsToAssociateWith = null; + for (var subgoal : goal.getSubgoals()) { + satisfyGoal(subgoal); + if(plan.getEvaluation().forGoal(subgoal).getScore() == 0 || !subgoal.shouldRollbackIfUnsatisfied()) { + var associatedActivities = plan.getEvaluation().forGoal(subgoal).getAssociatedActivities(); + var insertedActivities = plan.getEvaluation().forGoal(subgoal).getInsertedActivities(); + var aggregatedActivities = new ArrayList(); + aggregatedActivities.addAll(associatedActivities); + aggregatedActivities.addAll(insertedActivities); + if (!aggregatedActivities.isEmpty() && + (goal.getOptimizer().isBetterThanCurrent(aggregatedActivities) || + currentSatisfiedGoal == null)) { + actsToInsert = insertedActivities; + actsToAssociateWith = associatedActivities; + currentSatisfiedGoal = subgoal; } - rollback(subgoal); } - //we should have the best solution - if (currentSatisfiedGoal != null) { - final var insertionResult = checkAndInsertActs(actsToInsert); - final var goalEvaluation = plan.getEvaluation().forGoal(goal); - if(insertionResult.success()) { - for(var act: insertionResult.activitiesInserted()){ - goalEvaluation.associate(act, false, null); - } - goalEvaluation.setConflictSatisfaction(null, ConflictSatisfaction.SAT); - } else{ - rollback(currentSatisfiedGoal); - + rollback(subgoal); + } + //we should have the best solution + if (currentSatisfiedGoal != null) { + final var insertionResult = checkAndInsertActs(actsToInsert); + final var goalEvaluation = plan.getEvaluation().forGoal(goal); + if(insertionResult.success()) { + for(var act: insertionResult.activitiesInserted()){ + goalEvaluation.associate(act, false, null); } - } else { - plan.getEvaluation().forGoal(goal).setConflictSatisfaction(null, ConflictSatisfaction.NOT_SAT); + goalEvaluation.setConflictSatisfaction(null, ConflictSatisfaction.SAT); + } else{ + rollback(currentSatisfiedGoal); + } } else { - var atLeastOneSatisfied = false; - //just satisfy any goal - for (var subgoal : goal.getSubgoals()) { - satisfyGoal(subgoal); - final var evaluation = plan.getEvaluation(); - final var subgoalIsSatisfied = (evaluation.forGoal(subgoal).getSatisfaction() == ConflictSatisfaction.SAT); - evaluation.forGoal(goal).associate(evaluation.forGoal(subgoal).getAssociatedActivities(), false, null); - evaluation.forGoal(goal).associate(evaluation.forGoal(subgoal).getInsertedActivities(), true, null); - if(subgoalIsSatisfied){ - logger.info("OR goal " + goal.getName() + ": subgoal " + subgoal.getName() + " has been satisfied, stopping"); - atLeastOneSatisfied = true; - break; - } - logger.info("OR goal " + goal.getName() + ": subgoal " + subgoal.getName() + " could not be satisfied, moving on to next subgoal"); + plan.getEvaluation().forGoal(goal).setConflictSatisfaction(null, ConflictSatisfaction.NOT_SAT); + } + } else { + var atLeastOneSatisfied = false; + //just satisfy any goal + for (var subgoal : goal.getSubgoals()) { + satisfyGoal(subgoal); + final var evaluation = plan.getEvaluation(); + final var subgoalIsSatisfied = (evaluation.forGoal(subgoal).getSatisfaction() == ConflictSatisfaction.SAT); + evaluation.forGoal(goal).associate(evaluation.forGoal(subgoal).getAssociatedActivities(), false, null); + evaluation.forGoal(goal).associate(evaluation.forGoal(subgoal).getInsertedActivities(), true, null); + if(subgoalIsSatisfied){ + logger.info("OR goal " + goal.getName() + ": subgoal " + subgoal.getName() + " has been satisfied, stopping"); + atLeastOneSatisfied = true; + break; } - if(atLeastOneSatisfied){ - plan.getEvaluation().forGoal(goal).setConflictSatisfaction(null, ConflictSatisfaction.SAT); - } else { - plan.getEvaluation().forGoal(goal).setConflictSatisfaction(null, ConflictSatisfaction.NOT_SAT); - if(goal.shouldRollbackIfUnsatisfied()) { - for (var subgoal : goal.getSubgoals()) { - rollback(subgoal); - } + logger.info("OR goal " + goal.getName() + ": subgoal " + subgoal.getName() + " could not be satisfied, moving on to next subgoal"); + } + if(atLeastOneSatisfied){ + plan.getEvaluation().forGoal(goal).setConflictSatisfaction(null, ConflictSatisfaction.SAT); + } else { + plan.getEvaluation().forGoal(goal).setConflictSatisfaction(null, ConflictSatisfaction.NOT_SAT); + if(goal.shouldRollbackIfUnsatisfied()) { + for (var subgoal : goal.getSubgoals()) { + rollback(subgoal); } } } + } } private void rollback(Goal goal){ @@ -485,7 +485,7 @@ private void satisfyGoalGeneral(Goal goal) throws SchedulingInterruptedException final var alreadyTried = new ArrayList(); int i = 0; final var itConflicts = missingConflicts.iterator(); - //create new activity instances for each missing conflict + //create new activity instances for each missing conflict while (itConflicts.hasNext()) { final var missing = itConflicts.next(); assert missing != null; @@ -521,7 +521,7 @@ private void satisfyGoalGeneral(Goal goal) throws SchedulingInterruptedException private ConflictSolverResult solveMissingRecurrenceConflict( final MissingRecurrenceConflict missingRecurrenceConflict, final Goal goal - ) throws SchedulingInterruptedException + ) throws SchedulingInterruptedException { Optional maxIterations = Optional.empty(); final var spaceToFill = (missingRecurrenceConflict.nextStart.minus(missingRecurrenceConflict.lastStart)); @@ -739,7 +739,7 @@ private ConflictSolverResult solveActivityTemplateConflict( final MissingActivityTemplateConflict missingActivityTemplateConflict, final Goal goal, final boolean isSubconflict - ) + ) throws SchedulingInterruptedException { var sat = ConflictSatisfaction.NOT_SAT; @@ -810,8 +810,8 @@ private ConflictSolverResult solveMissingAssociationConflict( // In that case, a new activity must be created as a copy of act but including the anchorId. This activity is then added to all appropriate data structures and the association is created if (missingAssociationConflict.getAnchorIdTo().isPresent()) { SchedulingActivity predecessor = plan.getActivitiesById().get(missingAssociationConflict - .getAnchorIdTo() - .get()); + .getAnchorIdTo() + .get()); Duration startOffset = act.startOffset().minus(plan.calculateAbsoluteStartOffsetAnchoredActivity( predecessor)); // In case the goal requires generation of anchors, then check that the anchor is to the Start. Otherwise (anchor to End), make sure that there is a positive offset @@ -849,7 +849,7 @@ private ConflictSolverResult solveMissingAssociationConflict( + " has been associated to goal " + goal.getName() + " to satisfy conflict " - ); + ); break; } } else { @@ -1088,11 +1088,11 @@ private SimulationData getLatestSimResultsUpTo(final Duration time, final Set Date: Tue, 27 Aug 2024 17:04:41 -0700 Subject: [PATCH 034/108] Add double constructors for Duration --- .../aerie/merlin/protocol/types/Duration.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java b/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java index de287d2efc..fea63c43dd 100644 --- a/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java +++ b/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java @@ -152,6 +152,7 @@ public record Duration(long micros) implements Comparable { public static final Duration MINUTE = SECOND.times(60); /** One hour (h), equal to 60m. */ public static final Duration HOUR = MINUTE.times(60); + /** One day (d), equal to 24h. */ public static final Duration DAY = HOUR.times(24); /** The unit of measurement for microseconds. */ @@ -164,26 +165,59 @@ public record Duration(long micros) implements Comparable { public static final Duration MINUTES = MINUTE; /** The unit of measurement for hours. */ public static final Duration HOURS = HOUR; + /** The unit of measurement for days. */ public static final Duration DAYS = DAY; + /** Constructs a duration from an exact number of microseconds. */ public static Duration microseconds(final long quantity) throws ArithmeticException { return MICROSECOND.times(quantity); } + + /** Constructs a duration from an exact number of milliseconds. */ public static Duration milliseconds(final long quantity) throws ArithmeticException { return MILLISECOND.times(quantity); } + /** Constructs a duration from a double of milliseconds, rounding to the nearest microsecond. */ + public static Duration milliseconds(final double quantity) { + return roundNearest(quantity, MILLISECOND); + } + + /** Constructs a duration from an exact number of seconds. */ public static Duration seconds(final long quantity) throws ArithmeticException { return SECOND.times(quantity); } + /** Constructs a duration from a double of seconds, rounding to the nearest microsecond. */ + public static Duration seconds(final double quantity) { + return roundNearest(quantity, SECOND); + } + + /** Constructs a duration from an exact number of minutes. */ public static Duration minutes(final long quantity) throws ArithmeticException { return MINUTE.times(quantity); } + /** Constructs a duration from a double of minutes, rounding to the nearest microsecond. */ + public static Duration minutes(final double quantity) { + return roundNearest(quantity, MINUTE); + } + + /** Constructs a duration from an exact number of hours. */ public static Duration hours(final long quantity) throws ArithmeticException { return HOUR.times(quantity); } + /** Constructs a duration from a double of hours, rounding to the nearest microsecond. */ + public static Duration hours(final double quantity) { + return roundNearest(quantity, HOUR); + } + + /** Constructs a duration from an exact number of days. */ public static Duration days(final long quantity) throws ArithmeticException { return DAY.times(quantity); } + /** Constructs a duration from a double of days, rounding to the nearest microsecond. */ + public static Duration days(final double quantity) { + return roundNearest(quantity, DAY); + } + /** Construct a duration in terms of a multiple of some unit. */ public static Duration of(final long quantity, final Duration unit) { From 5bd5d8b15312e5ab9410e4c5fc96300179a13a89 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Tue, 27 Aug 2024 17:05:33 -0700 Subject: [PATCH 035/108] Misc timeline improvements and bug fixes --- .../procedural/timeline/ops/GeneralOps.kt | 2 +- .../timeline/payloads/activities/Directive.kt | 18 +++++++++--------- .../aerie/procedural/timeline/util/Coalesce.kt | 2 +- .../plan/SchedulerToProcedurePlanAdapter.kt | 7 +++++-- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt index a09b6a657a..b833bd8e18 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt @@ -259,7 +259,7 @@ interface GeneralOps, THIS: GeneralOps>: Timeline, truncateMarginal: Boolean = true) = if (truncateMarginal) { unsafeMap2(ctor, windows) { l, _, i -> l.withNewInterval(i) } } else { diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt index a1ccc1395c..12fc360bab 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt @@ -7,18 +7,18 @@ import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId /** A wrapper of any type of activity directive containing common data. */ data class Directive( /** The inner payload, typically either [AnyDirective] or a mission model activity type. */ - @JvmField val inner: A, + @JvmField val inner: A, /** The name of this specific directive. */ - @JvmField val name: String, + @JvmField val name: String, /** The directive id. */ - @JvmField val id: ActivityDirectiveId, + @JvmField val id: ActivityDirectiveId, override val type: String, /** The start behavior for this directive. */ - val start: DirectiveStart, + val start: DirectiveStart, ): Activity> { override val startTime: Duration get() = when (start) { @@ -36,10 +36,10 @@ data class Directive( /** Transform the inner payload with a function, returning a new directive object. */ fun mapInner(/***/ f: (A) -> R) = Directive( - f(inner), - name, - id, - type, - start + f(inner), + name, + id, + type, + start ) } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Coalesce.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Coalesce.kt index 5d2df96298..ce9f732c1b 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Coalesce.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Coalesce.kt @@ -24,7 +24,7 @@ fun > coalesceList(list: List, shouldCoalesce: I.(I) -> Bo var shortIndex = 0 var startIndex = 0 while(mutableList[startIndex].interval.isEmpty()) startIndex++ - var buffer = mutableList[shortIndex] + var buffer = mutableList[startIndex] for (segment in mutableList.subList(startIndex + 1, mutableList.size)) { if (segment.interval.isEmpty()) continue val comparison = buffer.interval.compareEndToStart(segment.interval) diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt index bca7a2a66b..b7c7b46674 100644 --- a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt @@ -26,8 +26,11 @@ data class SchedulerToProcedurePlanAdapter( override fun toAbsolute(rel: Duration) = planningHorizon.startInstant + rel override fun directives(type: String?, deserializer: (SerializedValue) -> A): Directives { - val schedulerActivities = (if (type == null) schedulerPlan.activities else schedulerPlan.activitiesByType[ActivityType(type)]) - ?: throw Exception("could not find activities by type $type") + val schedulerActivities = if (type == null) { + this.schedulerPlan.activities + } else { + this.schedulerPlan.activities.filter { it.type.name == type } + } val result = ArrayList>(schedulerActivities.size) for (activity in schedulerActivities) { From e0df9f640afdc5078606a48192536aa532ab2d13 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 28 Aug 2024 17:15:52 -0700 Subject: [PATCH 036/108] Fix out of bounds error in coalesce --- .../aerie/procedural/timeline/util/Coalesce.kt | 4 ++-- .../aerie/procedural/timeline/util/CoalesceTest.kt | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Coalesce.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Coalesce.kt index ce9f732c1b..2396ec9e05 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Coalesce.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/Coalesce.kt @@ -22,8 +22,8 @@ fun > coalesceList(list: List, shouldCoalesce: I.(I) -> Bo val mutableList = list.toMutableList() if (mutableList.isEmpty()) return mutableList var shortIndex = 0 - var startIndex = 0 - while(mutableList[startIndex].interval.isEmpty()) startIndex++ + val startIndex = mutableList.indexOfFirst { !it.interval.isEmpty() } + if (startIndex == -1) return listOf(); var buffer = mutableList[startIndex] for (segment in mutableList.subList(startIndex + 1, mutableList.size)) { if (segment.interval.isEmpty()) continue diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/CoalesceTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/CoalesceTest.kt index 4c6db6b675..6e02688c73 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/CoalesceTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/CoalesceTest.kt @@ -1,5 +1,6 @@ package gov.nasa.ammos.aerie.procedural.timeline.util +import gov.nasa.ammos.aerie.procedural.timeline.Interval import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.at import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between @@ -36,4 +37,16 @@ class CoalesceTest { assertIterableEquals(expected, result) } + + @Test + fun allIntervalsEmpty() { + val result = coalesceList(listOf( + Segment(Interval.EMPTY, false), + Segment(seconds(1) .. seconds(-1), true) + ), Segment::valueEquals) + + val expected = listOf>() + + assertIterableEquals(expected, result) + } } From 69b37a4848c1ca327082a0916f705d92271df6c1 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 28 Aug 2024 17:16:23 -0700 Subject: [PATCH 037/108] Fix checkpoint sim up-to-date check --- .../CheckpointSimulationFacade.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java index 7b22b189ee..f5fb07efad 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java @@ -30,6 +30,7 @@ import static gov.nasa.jpl.aerie.merlin.driver.CheckpointSimulationDriver.onceAllActivitiesAreFinished; import static gov.nasa.jpl.aerie.scheduler.simulation.SimulationFacadeUtils.scheduleFromPlan; +import static gov.nasa.jpl.aerie.scheduler.simulation.SimulationFacadeUtils.schedulingActToActivityDir; import static gov.nasa.jpl.aerie.scheduler.simulation.SimulationFacadeUtils.updatePlanWithChildActivities; public class CheckpointSimulationFacade implements SimulationFacade { @@ -308,12 +309,24 @@ public SimulationData simulateWithResults( final Plan plan, final Duration until, final Set resourceNames - ) throws SimulationException, SchedulingInterruptedException - { + ) throws SimulationException, SchedulingInterruptedException { if (this.initialSimulationResults != null) { final var inputPlan = scheduleFromPlan(plan, schedulerModel); - final var initialPlanA = scheduleFromPlan(this.initialSimulationResults.plan(), schedulerModel); - if (initialPlanA.equals(inputPlan)) { + final HashMap planActuallySimulated = new HashMap<>(); + final var initialPlan = this.initialSimulationResults.plan().getActivitiesById(); + var allActivitiesFound = true; + for (final var activityInstance: this.initialSimulationResults.constraintsResults().activities) { + if (activityInstance.directiveId().isPresent()) { + final var directiveId = activityInstance.directiveId().get(); + final var directive = initialPlan.get(directiveId); + if (directive != null) { + planActuallySimulated.put(activityInstance.directiveId().get(), schedulingActToActivityDir(directive, schedulerModel)); + } else { + allActivitiesFound = false; + } + } + } + if (allActivitiesFound && inputPlan.equals(new PlanSimCorrespondence(planActuallySimulated))) { return initialSimulationResults; } } @@ -332,6 +345,6 @@ public Optional getLatestSimulationData() { if (this.latestSimulationData == null) return Optional.ofNullable(this.initialSimulationResults); else - return Optional.ofNullable(this.latestSimulationData); + return Optional.of(this.latestSimulationData); } } From 23acb8a216d79b8a4227626a0e8c5a95c4175c5b Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 28 Aug 2024 17:16:39 -0700 Subject: [PATCH 038/108] Improve uncommitted activities error --- .../main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java index fe5cc954b5..2e94d9debe 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java @@ -72,7 +72,7 @@ public void run(Evaluation eval, Plan plan, MissionModel missionModel, Functi procedureMapper.deserialize(this.args).run(editablePlan); if (!editablePlan.getUncommittedChanges().isEmpty()) { - throw new NotImplementedException("emit warning"); + throw new IllegalStateException("procedural goal %s had changes that were not committed or rolled back".formatted(jarPath.getFileName())); } for (final var edit : editablePlan.getTotalDiff()) { if (edit instanceof Edit.Create c) { From e72eebaee0be67b14afca421ca89ca62dac56713 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 28 Aug 2024 17:17:41 -0700 Subject: [PATCH 039/108] Fix activity start time bug --- .../nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt index b076c0d009..2471a878b7 100644 --- a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt @@ -104,7 +104,10 @@ data class InMemoryEditablePlan( is DirectiveStart.Absolute -> null is DirectiveStart.Anchor -> s.parentId }, - start is DirectiveStart.Anchor && (start as DirectiveStart.Anchor).anchorPoint == DirectiveStart.Anchor.AnchorPoint.Start, + when (val s = start) { + is DirectiveStart.Absolute -> true + is DirectiveStart.Anchor -> s.anchorPoint == DirectiveStart.Anchor.AnchorPoint.Start + }, isNew ) } From 178bc4ac1451b88b6e6b44eb87848e45de185e1d Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 29 Aug 2024 11:31:14 -0700 Subject: [PATCH 040/108] Remove duplicate activites --- .../main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java | 1 - 1 file changed, 1 deletion(-) diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java index 2e94d9debe..18a91aada2 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java @@ -84,7 +84,6 @@ public void run(Evaluation eval, Plan plan, MissionModel missionModel, Functi final var evaluation = eval.forGoal(this); for (final var activity : newActivities) { - plan.add(activity); evaluation.associate(activity, true, null); } evaluation.setConflictSatisfaction(null, ConflictSatisfaction.SAT); From 391cb940b0383aa93892871bebc86a0f872044af Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 29 Aug 2024 12:04:17 -0700 Subject: [PATCH 041/108] Set expected duration --- .../aerie/scheduler/plan/InMemoryEditablePlan.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt index 2471a878b7..1f3442fcb5 100644 --- a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt @@ -14,6 +14,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId +import gov.nasa.jpl.aerie.merlin.protocol.types.DurationType import gov.nasa.jpl.aerie.scheduler.DirectiveIdGenerator import gov.nasa.jpl.aerie.scheduler.model.* import java.time.Instant @@ -97,7 +98,18 @@ data class InMemoryEditablePlan( is DirectiveStart.Absolute -> s.time is DirectiveStart.Anchor -> s.offset }, - Duration.ZERO, + when (val d = lookupActivityType(type).durationType) { + is DurationType.Controllable -> { + inner.arguments[d.parameterName]?.asInt()?.let { Duration(it.get()) } + } + is DurationType.Parametric -> { + d.durationFunction.apply(inner.arguments) + } + is DurationType.Fixed -> { + d.duration + } + else -> Duration.ZERO + }, inner.arguments, null, when (val s = start) { From addbabe3d0bf0b7009b0c9dd5d94bb183403dfdc Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 29 Aug 2024 12:04:30 -0700 Subject: [PATCH 042/108] Validate activity arguments --- .../nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt index 1f3442fcb5..a90c9b9abd 100644 --- a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt @@ -17,6 +17,7 @@ import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId import gov.nasa.jpl.aerie.merlin.protocol.types.DurationType import gov.nasa.jpl.aerie.scheduler.DirectiveIdGenerator import gov.nasa.jpl.aerie.scheduler.model.* +import gov.nasa.jpl.aerie.scheduler.plan.InMemoryEditablePlan.Companion.validateArguments import java.time.Instant import kotlin.jvm.optionals.getOrNull import kotlin.math.absoluteValue @@ -56,6 +57,7 @@ data class InMemoryEditablePlan( } val resolved = directive.resolve(id, parent) uncommittedChanges.add(Edit.Create(resolved)) + resolved.validateArguments(lookupActivityType) plan.add(resolved.toSchedulingActivityDirective(lookupActivityType, true)) return id } @@ -91,6 +93,10 @@ data class InMemoryEditablePlan( override fun toAbsolute(rel: Duration) = plan.toAbsolute(rel) companion object { + fun Directive.validateArguments(lookupActivityType: (String) -> ActivityType) { + lookupActivityType(type).specType.inputType.validateArguments(inner.arguments) + } + @JvmStatic fun Directive.toSchedulingActivityDirective(lookupActivityType: (String) -> ActivityType, isNew: Boolean) = SchedulingActivity( id, lookupActivityType(type), From 11254f27918bcc7c58c8ef4780d1a59f5d263123 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 29 Aug 2024 12:04:48 -0700 Subject: [PATCH 043/108] Add complex test case --- .../fooprocedures/procedures/StayWellFed.java | 130 ++++++++++++++++++ .../timeline/collections/profiles/Real.kt | 15 ++ .../timeline/ops/SerialSegmentOps.kt | 2 +- 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java new file mode 100644 index 0000000000..ffc2048bf7 --- /dev/null +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java @@ -0,0 +1,130 @@ +package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.procedures; + +import gov.nasa.ammos.aerie.procedural.scheduling.Rule; +import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure; +import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan; +import gov.nasa.ammos.aerie.procedural.timeline.Interval; +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real; +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Strings; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.LinearEquation; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart; +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * This goal creates regularly spaced Bite Banana activities (to stay well-fed). + * This will cause the /fruit resource to drop far below zero, so it also creates + * Grow Banana activities to fix that. + * + * More detailed algorithm: + * 1. The goal is only applied when /producer == Dole. This is analogous to a mission phase. + * 2. During the Dole phase, place a Bite Banana at most every `bitePeriodHours` apart. + * This step accounts for existing Bite Bananas and is essentially a cheap reimplementation + * of Recurrence Goal. + * 3. Resimulate to see the effects this has on the /fruit resource + * 4. We now want to prevent /fruit from dropping below 0. We could do this by placing + * a Grow Banana before the first time it becomes negative, and then resimulate, but + * that would take a lot of simulations. So instead, we place the activity and then + * mock the effect model of Grow Banana by adding a slanted step function to /fruit; + * then iterate. + * + * Since we are doing this all in one goal, we know that we just created these Bites, + * and can safely anchor the new Grows to the Bites, if there is one. So at each + * `/fruit < 0` point, we query for a bite banana and anchor to it if possible. + * @param bitePeriodHours + */ +@SchedulingProcedure +public record StayWellFed(double bitePeriodHours) implements Rule { + @Override + public void run(@NotNull final EditablePlan plan) { + final var bitePeriod = Duration.hours(bitePeriodHours); + var simResults = plan.latestResults(); + if (simResults == null) simResults = plan.simulate(); + + // I'm using producer as a substitute for a mission phase variable. + // This goal will only apply during the Dole mission phase. :) + final var dolePhase = simResults + .resource("/producer", Strings::deserialize) + .highlightEqualTo("Dole"); + dolePhase.cache(); + + // Manual recurrence goal: during Dole phase, require a bitebanana every `bitePeriod`. + final var bites = plan.directives("BiteBanana") + .filterByWindows(dolePhase, false) + .collect(); + + var currentTime = Duration.MIN_VALUE; + for (final var phase: dolePhase.collect()) { + currentTime = Duration.max(currentTime, phase.start); + + while (currentTime.plus(bitePeriod).shorterThan(phase.end)) { + var nextExistingBiteTime = bites.isEmpty() ? Duration.MAX_VALUE : bites.getFirst().getStartTime(); + while (nextExistingBiteTime.minus(currentTime).noLongerThan(bitePeriod)) { + currentTime = Duration.max(currentTime, nextExistingBiteTime); + bites.removeFirst(); + + nextExistingBiteTime = bites.isEmpty() ? Duration.MAX_VALUE : bites.getFirst().getStartTime(); + } + while (nextExistingBiteTime.minus(currentTime).longerThan(bitePeriod) && phase.contains(currentTime.plus(bitePeriod))) { + currentTime = currentTime.plus(bitePeriod); + plan.create( + "BiteBanana", + new DirectiveStart.Absolute(currentTime), + Map.of("biteSize", SerializedValue.of(1)) + ); + } + } + } + + plan.commit(); + simResults = plan.simulate(); + + final var newBites = plan.directives("BiteBanana") + .filterByWindows(dolePhase, false); + + // All this banana biting made us run out of bananas. + // So we iteratively find the first time /fruit drops below zero + // and add a grow banana fix it. We then mock the effect of grow banana + // by adding one to /fruit, rather than resimulating, and do it again. + var fruit = simResults.resource("/fruit", Real::deserialize); + fruit.cache(); + + var ranOutAt = fruit.lessThan(0).filterByWindows(dolePhase, true).risingEdges().highlightTrue().collect(); + while (!ranOutAt.isEmpty()) { + final var problemStart = ranOutAt.getFirst().start; + final var growStart = problemStart.minus(Duration.HOUR); + final var activeBites = newBites.collect(Interval.at(problemStart)); + + final var currentFruit = fruit.sample(problemStart); + final var pastFruit = fruit.sample(growStart); + + final var growQuantity = Math.ceil(pastFruit - currentFruit); + + plan.create( + "GrowBanana", + activeBites.isEmpty() ? new DirectiveStart.Absolute(growStart) + : new DirectiveStart.Anchor(activeBites.getFirst().id, Duration.HOUR.negate(), DirectiveStart.Anchor.AnchorPoint.Start), + Map.of( + "growingDuration", SerializedValue.of(Duration.HOUR.micros()), + "quantity", SerializedValue.of(growQuantity) + ) + ); + + fruit = fruit.plus( + Real.step(growStart, growQuantity) + .set(new Real(List.of( + new Segment<>(Interval.between(growStart, problemStart), new LinearEquation(growStart, 0.0, growQuantity / (Duration.HOUR.in(Duration.SECONDS)))) + ))) + ); + + ranOutAt = fruit.lessThan(0).filterByWindows(dolePhase, true).risingEdges().highlightTrue().collect(); + } + + plan.commit(); + } +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt index dbe770e2ff..39e4560da8 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt @@ -13,6 +13,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.util.truncateList import gov.nasa.ammos.aerie.procedural.timeline.util.duration.unaryMinus import kotlin.jvm.optionals.getOrNull import kotlin.math.pow +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.*; /** A profile of [LinearEquations][LinearEquation]; a piece-wise linear real-number profile. */ data class Real(private val timeline: Timeline, Real>): @@ -217,5 +218,19 @@ data class Real(private val timeline: Timeline, Real>): } /***/ class RealDeserializeException(message: String): Exception(message) + + /** + * Creates a real profile step function. + * + * The result will be `0` on the interval `[Duration.MIN_VALUE, stepTime)`, + * then will be `value` on the interval `[stepTime, Duration.MAX_VALUE]`. + * + * @param stepTime time the step occurs (default [Duration.ZERO]) + * @param value value the function steps to (default `1.0`) + */ + @JvmStatic @JvmOverloads fun step(stepTime: Duration = Duration.ZERO, value: Double = 1.0) = Real( + Segment(Duration.MIN_VALUE ..< stepTime, LinearEquation(0.0)), + Segment(stepTime .. Duration.MAX_VALUE, LinearEquation(value)) + ) } } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOps.kt index 98b4235d6d..9c16a2c09c 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/SerialSegmentOps.kt @@ -23,7 +23,7 @@ interface SerialSegmentOps>: SerialOps< /** [(DOC)][assignGaps] Fills in gaps in this profile with another profile. */ // While this is logically the converse of [set], they can't delegate to each other because it would mess up the return type. infix fun assignGaps(other: SerialSegmentOps) = - map2OptionalValues(other, NullBinaryOperation.combineOrIdentity { l, _, _, -> l }) + map2OptionalValues(other, NullBinaryOperation.combineOrIdentity { l, _, _ -> l }) /** [(DOC)][assignGaps] Fills in gaps in this profile with a constant value. */ infix fun assignGaps(v: V) = assignGaps(Constants(v)) From e95fdd1f208df8ff78035eb2abebe6aa3901c1c0 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 29 Aug 2024 16:13:25 -0700 Subject: [PATCH 044/108] Rename Rule to Goal --- .../examples/fooprocedures/procedures/SampleProcedure.java | 4 ++-- .../examples/fooprocedures/procedures/SimulationDemo.java | 4 ++-- .../examples/fooprocedures/procedures/StayWellFed.java | 4 ++-- .../ammos/aerie/procedural/scheduling/{Rule.kt => Goal.kt} | 2 +- .../nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) rename procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/{Rule.kt => Goal.kt} (95%) diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java index 6163a7eec2..5f56a43e6c 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java @@ -1,7 +1,7 @@ package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.procedures; import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan; -import gov.nasa.ammos.aerie.procedural.scheduling.Rule; +import gov.nasa.ammos.aerie.procedural.scheduling.Goal; import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart; @@ -9,7 +9,7 @@ import java.util.Map; @SchedulingProcedure -public record SampleProcedure(int quantity) implements Rule { +public record SampleProcedure(int quantity) implements Goal { @Override public void run(EditablePlan plan) { final var firstTime = Duration.hours(24); diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java index 6e79616ee8..c619a01b02 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java @@ -2,7 +2,7 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; -import gov.nasa.ammos.aerie.procedural.scheduling.Rule; +import gov.nasa.ammos.aerie.procedural.scheduling.Goal; import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure; import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan; import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real; @@ -15,7 +15,7 @@ import java.util.Map; @SchedulingProcedure -public record SimulationDemo(int quantity) implements Rule { +public record SimulationDemo(int quantity) implements Goal { @Override public void run(EditablePlan plan) { // final var firstActivityTime = plan.toRelative(Instant.from(DOY_WITHOUT_ZONE_FORMATTER.parse("2024-128T07:00:00"))); diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java index ffc2048bf7..c01806953f 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java @@ -1,6 +1,6 @@ package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.procedures; -import gov.nasa.ammos.aerie.procedural.scheduling.Rule; +import gov.nasa.ammos.aerie.procedural.scheduling.Goal; import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure; import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan; import gov.nasa.ammos.aerie.procedural.timeline.Interval; @@ -39,7 +39,7 @@ * @param bitePeriodHours */ @SchedulingProcedure -public record StayWellFed(double bitePeriodHours) implements Rule { +public record StayWellFed(double bitePeriodHours) implements Goal { @Override public void run(@NotNull final EditablePlan plan) { final var bitePeriod = Duration.hours(bitePeriodHours); diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/Rule.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/Goal.kt similarity index 95% rename from procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/Rule.kt rename to procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/Goal.kt index 4332e3f12d..f4014d2cf0 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/Rule.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/Goal.kt @@ -3,7 +3,7 @@ package gov.nasa.ammos.aerie.procedural.scheduling import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan /** The interface that all scheduling rules must satisfy. */ -interface Rule { +interface Goal { /** * Run the rule. * diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt index 6e5c9e3e16..3340317cb3 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt @@ -3,7 +3,7 @@ package gov.nasa.ammos.aerie.procedural.scheduling import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema -interface ProcedureMapper { +interface ProcedureMapper { fun valueSchema(): ValueSchema fun serialize(procedure: T): SerializedValue fun deserialize(arguments: SerializedValue): T From d1525932e11aaa21da42e307c9fc0ca5fb8a9cf4 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Tue, 3 Sep 2024 10:49:41 -0700 Subject: [PATCH 045/108] bump migration ids --- .../Aerie/{9_goal_invocations => 10_goal_invocations}/down.sql | 2 +- .../Aerie/{9_goal_invocations => 10_goal_invocations}/up.sql | 2 +- .../down.sql | 2 +- .../up.sql | 2 +- deployment/postgres-init-db/sql/applied_migrations.sql | 1 + 5 files changed, 5 insertions(+), 4 deletions(-) rename deployment/hasura/migrations/Aerie/{9_goal_invocations => 10_goal_invocations}/down.sql (98%) rename deployment/hasura/migrations/Aerie/{9_goal_invocations => 10_goal_invocations}/up.sql (98%) rename deployment/hasura/migrations/Aerie/{10_procedural_scheduling => 11_procedural_scheduling}/down.sql (95%) rename deployment/hasura/migrations/Aerie/{10_procedural_scheduling => 11_procedural_scheduling}/up.sql (97%) diff --git a/deployment/hasura/migrations/Aerie/9_goal_invocations/down.sql b/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql similarity index 98% rename from deployment/hasura/migrations/Aerie/9_goal_invocations/down.sql rename to deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql index 6aa4d6fef2..5d021f4f30 100644 --- a/deployment/hasura/migrations/Aerie/9_goal_invocations/down.sql +++ b/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql @@ -90,4 +90,4 @@ alter table scheduler.scheduling_specification_goals comment on column scheduler.scheduling_goal_definition.definition is e'' 'An executable expression in the Merlin scheduling language.'; -call migrations.mark_migration_rolled_back('9'); +call migrations.mark_migration_rolled_back('10'); diff --git a/deployment/hasura/migrations/Aerie/9_goal_invocations/up.sql b/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql similarity index 98% rename from deployment/hasura/migrations/Aerie/9_goal_invocations/up.sql rename to deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql index f2d7f4d32c..027e93724c 100644 --- a/deployment/hasura/migrations/Aerie/9_goal_invocations/up.sql +++ b/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql @@ -87,4 +87,4 @@ alter table scheduler.scheduling_goal_analysis_satisfying_activities on update cascade on delete cascade; -call migrations.mark_migration_applied('9'); +call migrations.mark_migration_applied('10'); diff --git a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql b/deployment/hasura/migrations/Aerie/11_procedural_scheduling/down.sql similarity index 95% rename from deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql rename to deployment/hasura/migrations/Aerie/11_procedural_scheduling/down.sql index 6d913616ae..e685118169 100644 --- a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql +++ b/deployment/hasura/migrations/Aerie/11_procedural_scheduling/down.sql @@ -32,4 +32,4 @@ comment on column scheduler.scheduling_goal_definition.definition is e'' drop type scheduler.goal_type; -call migrations.mark_migration_rolled_back('10'); +call migrations.mark_migration_rolled_back('11'); diff --git a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql b/deployment/hasura/migrations/Aerie/11_procedural_scheduling/up.sql similarity index 97% rename from deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql rename to deployment/hasura/migrations/Aerie/11_procedural_scheduling/up.sql index d0b7a00730..1198e06b0b 100644 --- a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql +++ b/deployment/hasura/migrations/Aerie/11_procedural_scheduling/up.sql @@ -47,4 +47,4 @@ comment on column scheduler.scheduling_goal_analysis.arguments is e'' 'The "as run" arguments passed to this goal during the scheduling run.' 'Follows scheduler.scheduling_goal_definition.parameter_schema.'; -call migrations.mark_migration_applied('10'); +call migrations.mark_migration_applied('11'); diff --git a/deployment/postgres-init-db/sql/applied_migrations.sql b/deployment/postgres-init-db/sql/applied_migrations.sql index 245dc51deb..9f1d007a71 100644 --- a/deployment/postgres-init-db/sql/applied_migrations.sql +++ b/deployment/postgres-init-db/sql/applied_migrations.sql @@ -13,3 +13,4 @@ call migrations.mark_migration_applied('7'); call migrations.mark_migration_applied('8'); call migrations.mark_migration_applied('9'); call migrations.mark_migration_applied('10'); +call migrations.mark_migration_applied('11'); From 25e451c9fe331695364808c593ebbadb9e074170 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Tue, 3 Sep 2024 11:44:06 -0700 Subject: [PATCH 046/108] Rename Intervals to Universal The whole point is that it can contain any type, not just intervals --- .../procedural/constraints/Violations.kt | 4 +-- .../aerie/procedural/timeline/Timeline.kt | 2 +- .../{Intervals.kt => Universal.kt} | 11 ++++--- .../procedural/timeline/ops/BooleanOps.kt | 7 +++-- .../procedural/timeline/ops/GeneralOps.kt | 10 +++---- .../procedural/timeline/ops/ParallelOps.kt | 8 ++--- .../timeline/payloads/activities/Instance.kt | 14 ++++----- .../procedural/timeline/ops/GeneralOpsTest.kt | 30 +++++++++---------- .../timeline/ops/ParallelOpsTest.kt | 6 ++-- .../timeline/util/ListCollectorTest.kt | 10 +++---- 10 files changed, 52 insertions(+), 50 deletions(-) rename procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/{Intervals.kt => Universal.kt} (64%) diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt index dea3068dbd..831c2b4a1e 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt @@ -3,7 +3,7 @@ package gov.nasa.ammos.aerie.procedural.constraints import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline import gov.nasa.ammos.aerie.procedural.timeline.BoundsTransformer import gov.nasa.ammos.aerie.procedural.timeline.Timeline -import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.collections.Universal import gov.nasa.ammos.aerie.procedural.timeline.collections.Windows import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real import gov.nasa.ammos.aerie.procedural.timeline.ops.* @@ -52,7 +52,7 @@ data class Violations(private val timeline: Timeline): } /** Creates a [Violations] object that violates inside each interval. */ - @JvmStatic fun Windows.violateInside() = unsafeCast(::Intervals).violations() + @JvmStatic fun Windows.violateInside() = unsafeCast(::Universal).violations() /** Creates a [Violations] object that violates outside each interval. */ @JvmStatic fun Windows.violateOutside() = complement().violateInside() diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt index 7434394541..2178095f2f 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt @@ -37,7 +37,7 @@ interface Timeline, THIS: Timeline> { * The payload type [V] must be equal between the two types. * * This operation allows you to break the invariants of more specialized timeline types. For example, casting - * [`Intervals>`][gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals] to [Booleans][gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans] + * [`Intervals>`][gov.nasa.ammos.aerie.procedural.timeline.collections.Universal] to [Booleans][gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans] * without sorting and coalescing the segments could result in an invalid profile. In cases like that, it is better to * use [flattenIntoProfile][gov.nasa.ammos.aerie.procedural.timeline.ops.ParallelOps.flattenIntoProfile] or [reduceIntoProfile][gov.nasa.ammos.aerie.procedural.timeline.ops.ParallelOps.reduceIntoProfile]. */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Intervals.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Universal.kt similarity index 64% rename from procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Intervals.kt rename to procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Universal.kt index 40fcc68184..b502e5e1ee 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Intervals.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/Universal.kt @@ -5,15 +5,14 @@ import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline import gov.nasa.ammos.aerie.procedural.timeline.ops.ParallelOps import gov.nasa.ammos.aerie.procedural.timeline.Timeline import gov.nasa.ammos.aerie.procedural.timeline.ops.NonZeroDurationOps -import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceNoOp import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList /** A timeline of any [IntervalLike] object with no special operations. */ -data class Intervals>(private val timeline: Timeline>): - Timeline> by timeline, - ParallelOps>, - NonZeroDurationOps> +data class Universal>(private val timeline: Timeline>): + Timeline> by timeline, + ParallelOps>, + NonZeroDurationOps> { constructor(vararg intervals: T): this(intervals.asList()) - constructor(intervals: List): this(BaseTimeline(::Intervals, preprocessList(intervals, null))) + constructor(intervals: List): this(BaseTimeline(::Universal, preprocessList(intervals, null))) } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/BooleanOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/BooleanOps.kt index d137341bf2..2f399fe64d 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/BooleanOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/BooleanOps.kt @@ -24,8 +24,11 @@ interface BooleanOps>: ConstantOps { /** [(DOC)][falsifyLongerThan] Falsifies any `true` segments with durations longer than the given duration. */ fun falsifyLongerThan(dur: Duration) = falsifyByDuration(Duration.MIN_VALUE .. dur) - /** [(DOC)][isolateTrue] Creates an [Intervals] object with intervals whenever this profile is `true`. */ - fun isolateTrue() = isolate { it.value } + /** [(DOC)][isolateTrue] Creates an [Universal] object with intervals whenever this profile is `true`. */ + fun isolateTrue() = isolateEqualTo(true) + + /** [(DOC)][isolateFalse] Creates an [Universal] object with intervals whenever this profile is `false`. */ + fun isolateFalse() = isolateEqualTo(false) /** [(DOC)][highlightTrue] Creates an [Windows] object that highlights whenever this profile is `true`. */ fun highlightTrue() = highlight { it.value } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt index b833bd8e18..efc885b2a7 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOps.kt @@ -2,7 +2,7 @@ package gov.nasa.ammos.aerie.procedural.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.ammos.aerie.procedural.timeline.* -import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.collections.Universal import gov.nasa.ammos.aerie.procedural.timeline.collections.Windows import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment @@ -122,11 +122,11 @@ interface GeneralOps, THIS: GeneralOps>: Timeline Boolean) = filter(false, f).unsafeCast(::Intervals) + fun isolate(f: (V) -> Boolean) = filter(false, f).unsafeCast(::Universal) /** * [(DOC)][highlight] Similar to [filter], but produces a coalesced [Windows] object @@ -135,7 +135,7 @@ interface GeneralOps, THIS: GeneralOps>: Timeline Boolean) = - unsafeMap(::Intervals, BoundsTransformer.IDENTITY, false) { + unsafeMap(::Universal, BoundsTransformer.IDENTITY, false) { if (f(it)) it.interval else Interval.EMPTY }.convert(::Windows) @@ -263,7 +263,7 @@ interface GeneralOps, THIS: GeneralOps>: Timeline l.withNewInterval(i) } } else { - unsafeMap2(::Intervals, windows) { l, _, _ -> l }.unsafeOperate { collect(it).distinct() }.unsafeCast(ctor) + unsafeMap2(::Universal, windows) { l, _, _ -> l }.unsafeOperate { collect(it).distinct() }.unsafeCast(ctor) } /** [(DOC)][shift] Uniformly shifts the entire timeline in time (positive shifts toward the future). */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt index f658ae009d..24338fdba7 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt @@ -2,7 +2,7 @@ package gov.nasa.ammos.aerie.procedural.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.ammos.aerie.procedural.timeline.* -import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.collections.Universal import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceNoOp @@ -28,7 +28,7 @@ interface ParallelOps, THIS: ParallelOps>: GeneralOp } /** [(DOC)][merge] Combines this timeline with a single payload object by overlaying them. */ - infix fun merge(i: T) = merge(Intervals(i)) + infix fun merge(i: T) = merge(Universal(i)) /** * [(DOC)][mapIntoProfile] Maps the objects into a parallel profile. @@ -179,7 +179,7 @@ interface ParallelOps, THIS: ParallelOps>: GeneralOp } /** - * [(DOC)][connectTo] Creates an [Intervals] object of [Connections][Connection] that associate of this timeline's + * [(DOC)][connectTo] Creates an [Universal] object of [Connections][Connection] that associate of this timeline's * object to the (chronologically) next object in another timeline that starts after this one ends. * * For a connection from an object A (in this timeline) to an object B (in the other timeline), the interval of the @@ -196,7 +196,7 @@ interface ParallelOps, THIS: ParallelOps>: GeneralOp * @param connectToBounds whether to connect to the end of the bounds if the other timeline ends prematurely */ fun > connectTo(other: ParallelOps, connectToBounds: Boolean) = - unsafeOperate(::Intervals) { opts -> + unsafeOperate(::Universal) { opts -> val sortedFrom = collect(opts).sortedWith { l, r -> l.interval.compareEnds(r.interval) } val sortedTo = other.collect(opts).sortedWith { l, r -> l.interval.compareStarts(r.interval) } val result = mutableListOf>() diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt index 432bd4e732..09cee6a470 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt @@ -8,18 +8,18 @@ import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId /** A wrapper of any type of activity instance containing common data. */ data class Instance( /** The inner payload, typically either [AnyInstance] or a mission model activity type. */ - @JvmField val inner: A, + @JvmField val inner: A, override val type: String, /** The instance id. */ - @JvmField val id: ActivityInstanceId, + @JvmField val id: ActivityInstanceId, /** - * The maybe-null id of the directive associated with this instance. - * - * Will be `null` if this is a child activity. - */ - @JvmField val directiveId: ActivityDirectiveId?, + * The maybe-null id of the directive associated with this instance. + * + * Will be `null` if this is a child activity. + */ + @JvmField val directiveId: ActivityDirectiveId?, override val interval: Interval, ): Activity> { override val startTime: Duration diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOpsTest.kt index 4ca05bebfc..d9392bf6b6 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/GeneralOpsTest.kt @@ -9,7 +9,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.betweenClosedOpen import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Exclusive import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Inclusive -import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.collections.Universal import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Constants import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans @@ -57,7 +57,7 @@ class GeneralOpsTest { @Test fun inspect() { var count: Int? = null - val tl = Intervals( + val tl = Universal( at(seconds(1)), at(seconds(2)) ).inspect { @@ -73,7 +73,7 @@ class GeneralOpsTest { @Test fun unset() { - val result = Intervals( + val result = Universal( seconds(0) .. seconds(2), seconds(2) .. seconds(3), seconds(3) .. seconds(5), @@ -105,7 +105,7 @@ class GeneralOpsTest { @Test fun filterPreserveMargin() { - val intervals = Intervals( + val universal = Universal( between(seconds(-1), seconds(1)), seconds(1) .. seconds(4), seconds(4) .. seconds(8), @@ -114,7 +114,7 @@ class GeneralOpsTest { // without preserve margin assertIterableEquals( listOf(seconds(1) .. seconds(4)), - intervals.filter(false) { it.duration() >= seconds(2) } + universal.filter(false) { it.duration() >= seconds(2) } .collect(seconds(0) .. seconds(5)) ) @@ -126,22 +126,22 @@ class GeneralOpsTest { seconds(1) .. seconds(4), seconds(4) .. seconds(5), ), - intervals.filter(true) { it.duration() >= seconds(2) } + universal.filter(true) { it.duration() >= seconds(2) } .collect(seconds(0) .. seconds(5)) ) // with preserve margin, without truncate margin // notice that the marginal intervals are retained and NOT truncated later assertIterableEquals( - intervals.collect(), - intervals.filter(true) { it.duration() >= seconds(2) } + universal.collect(), + universal.filter(true) { it.duration() >= seconds(2) } .collect(CollectOptions(seconds(0) .. seconds(5), false)) ) } @Test fun map() { - val result = Intervals( + val result = Universal( at(seconds(1)), seconds(2) .. seconds(3) ).unsafeMap(::Booleans, BoundsTransformer.IDENTITY, false) { Segment(it.interval, it.interval.isPoint()) } @@ -158,7 +158,7 @@ class GeneralOpsTest { @Test fun shiftBoundsTransform() { - val intervals = Intervals( + val universal = Universal( between(seconds(-1), seconds(0)), seconds(2) .. seconds(4), ).shift(Duration.SECOND) @@ -170,28 +170,28 @@ class GeneralOpsTest { assertIterableEquals( expected, - intervals.collect() + universal.collect() ) assertIterableEquals( expected, - intervals.collect(seconds(0) .. seconds(5)) + universal.collect(seconds(0) .. seconds(5)) ) } @Test fun shiftOutOfBounds() { - val intervals = Intervals(at(seconds(3))).shift(seconds(3)) + val universal = Universal(at(seconds(3))).shift(seconds(3)) assertIterableEquals( listOf(), - intervals.collect(seconds(0) .. seconds(5)) + universal.collect(seconds(0) .. seconds(5)) ) } @Test fun flatMapTest() { - val result = Intervals( + val result = Universal( seconds(2) .. seconds(8), seconds(0) .. seconds(3) ) diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOpsTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOpsTest.kt index b9e72aeae3..996095df0c 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOpsTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOpsTest.kt @@ -8,7 +8,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.betweenClosed import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Exclusive import gov.nasa.ammos.aerie.procedural.timeline.Interval.Inclusivity.Inclusive import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment -import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.collections.Universal import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo import org.junit.jupiter.api.Assertions.assertIterableEquals @@ -17,7 +17,7 @@ import org.junit.jupiter.api.Test class ParallelOpsTest { @Test fun flattenIntoProfile() { - val result = Intervals( + val result = Universal( Segment(seconds(1) .. seconds(3), 1), Segment(seconds(0) .. seconds(2), 2), Segment(seconds(4) .. seconds(6), 5) @@ -35,7 +35,7 @@ class ParallelOpsTest { @Test fun reduceIntoProfile() { - val result = Intervals( + val result = Universal( Segment(seconds(1) .. seconds(4), 1), Segment(seconds(0) .. seconds(3), 2), Segment(seconds(4) .. seconds(6), 5), diff --git a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListCollectorTest.kt b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListCollectorTest.kt index bf2ccccf82..1eeff7d7b0 100644 --- a/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListCollectorTest.kt +++ b/procedural/timeline/src/test/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListCollectorTest.kt @@ -5,7 +5,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import gov.nasa.ammos.aerie.procedural.timeline.Interval.Companion.between -import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.collections.Universal import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo val bounds = (seconds(0) .. seconds(10)) @@ -17,7 +17,7 @@ class ListCollectorTest { seconds(2) .. seconds(3), seconds(4) .. seconds(5), ) - val result = Intervals(input).collect(bounds) + val result = Universal(input).collect(bounds) assertIterableEquals(input, result) } @@ -28,7 +28,7 @@ class ListCollectorTest { seconds(2) .. seconds(3), seconds(14) .. seconds(15), ) - val result = Intervals(input).collect(bounds) + val result = Universal(input).collect(bounds) val expected = listOf( seconds(2) .. seconds(3), @@ -43,7 +43,7 @@ class ListCollectorTest { seconds(2) .. seconds(3), seconds(9) .. seconds(11), ) - val result = Intervals(input).collect(bounds) + val result = Universal(input).collect(bounds) val expected = listOf( seconds(2) .. seconds(3), @@ -63,7 +63,7 @@ class ListCollectorTest { seconds(13) .. seconds(14) ) - val result = Intervals(input).collect(CollectOptions(bounds, false)) + val result = Universal(input).collect(CollectOptions(bounds, false)) val expected = listOf( between(seconds(-1), seconds(1)), From 8472e445a27c271788ad12003cee084a493a69ee Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Tue, 3 Sep 2024 11:44:43 -0700 Subject: [PATCH 047/108] Add extra highlight and split varieties --- .../procedural/timeline/collections/profiles/Real.kt | 7 +++++++ .../aerie/procedural/timeline/ops/BooleanOps.kt | 12 +++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt index 39e4560da8..8f06967158 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt @@ -3,6 +3,7 @@ package gov.nasa.ammos.aerie.procedural.timeline.collections.profiles import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.ammos.aerie.procedural.timeline.* +import gov.nasa.ammos.aerie.procedural.timeline.ops.GeneralOps import gov.nasa.ammos.aerie.procedural.timeline.ops.numeric.LinearOps import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment import gov.nasa.ammos.aerie.procedural.timeline.ops.numeric.SerialNumericOps @@ -179,6 +180,12 @@ data class Real(private val timeline: Timeline, Real>): override fun increases() = detectChangesInternal({ l, r -> l < r }, { it > 0.0}) override fun decreases() = detectChangesInternal({ l, r -> l > r }, { it < 0.0}) + /** + * Highlights intervals where the value is equal to a specific value. + * @see [GeneralOps.highlight] + */ + fun highlightEqualTo(value: Double) = equalTo(value).highlightTrue() + private class UnreachableValueAtException: Exception("internal error. a serial profile had multiple values at the same time.") /** Calculates the value of the profile at the given time. */ diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/BooleanOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/BooleanOps.kt index 2f399fe64d..ecb5a86a16 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/BooleanOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/BooleanOps.kt @@ -2,7 +2,7 @@ package gov.nasa.ammos.aerie.procedural.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.ammos.aerie.procedural.timeline.Interval -import gov.nasa.ammos.aerie.procedural.timeline.collections.Intervals +import gov.nasa.ammos.aerie.procedural.timeline.collections.Universal import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo /** @@ -31,8 +31,14 @@ interface BooleanOps>: ConstantOps { fun isolateFalse() = isolateEqualTo(false) /** [(DOC)][highlightTrue] Creates an [Windows] object that highlights whenever this profile is `true`. */ - fun highlightTrue() = highlight { it.value } + fun highlightTrue() = highlightEqualTo(true) + + /** [(DOC)][highlightFalse] Creates an [Windows] object that highlights whenever this profile is `false`. */ + fun highlightFalse() = highlightEqualTo(false) /** [(DOC)][splitTrue] Splits `true` segments into the given number of pieces (leaving `false` unchanged). */ - fun splitTrue(numPieces: Int) = split { if (it.value) numPieces else 1 } + fun splitTrue(numPieces: Int) = splitEqualTo(true, numPieces) + + /** [(DOC)][splitFalse] Splits `false` segments into the given number of pieces (leaving `true` unchanged). */ + fun splitFalse(numPieces: Int) = splitEqualTo(false, numPieces) } From 72b81d4406820c0be05076afcf7e37659257f2b2 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Tue, 3 Sep 2024 12:04:22 -0700 Subject: [PATCH 048/108] Fix one e2e test --- .../gov/nasa/jpl/aerie/e2e/SchedulingTests.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java index fa7fef6b45..478fb5c661 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.Objects; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -1079,13 +1080,14 @@ void executeSchedulingRunWithArguments() throws IOException { final var activities = plan.activityDirectives(); assertEquals(2, activities.size()); - final var first = activities.getFirst(); - assertEquals(first.type(), "BiteBanana"); - assertEquals(first.startOffset(), "24:00:00"); - final var second = activities.getLast(); - assertEquals(second.type(), "BiteBanana"); - assertEquals(second.startOffset(), "30:00:00"); + assertTrue(activities.stream().anyMatch( + $ -> Objects.equals($.type(), "BiteBanana") && Objects.equals($.startOffset(), "24:00:00") + )); + + assertTrue(activities.stream().anyMatch( + $ -> Objects.equals($.type(), "BiteBanana") && Objects.equals($.startOffset(), "30:00:00") + )); } } } From c261313a113b96d842b4b05b9600d227b774a182 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Tue, 3 Sep 2024 12:37:31 -0700 Subject: [PATCH 049/108] Mock activity instances in sim result injection test --- .../aerie/e2e/AutomaticValidationTests.java | 14 ++--- .../nasa/jpl/aerie/e2e/ConstraintsTests.java | 6 +- .../nasa/jpl/aerie/e2e/SchedulingTests.java | 61 +++++++++++++------ .../nasa/jpl/aerie/e2e/SimulationTests.java | 20 +++--- .../jpl/aerie/e2e/TimelineRemoteTests.java | 2 +- .../jpl/aerie/e2e/utils/HasuraRequests.java | 24 +++++++- 6 files changed, 86 insertions(+), 41 deletions(-) diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/AutomaticValidationTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/AutomaticValidationTests.java index 17d618e627..ba7d730b47 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/AutomaticValidationTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/AutomaticValidationTests.java @@ -69,7 +69,7 @@ void afterEach() throws IOException { @Test void validationSuccess() throws IOException, InterruptedException { - final var activityId = hasura.insertActivity( + final var activityId = hasura.insertActivityDirective( planId, "BiteBanana", "1h", @@ -82,7 +82,7 @@ void validationSuccess() throws IOException, InterruptedException { @Test void noSuchActivityType() throws IOException, InterruptedException { - final var activityId = hasura.insertActivity( + final var activityId = hasura.insertActivityDirective( planId, "NopeBanana", "1h", @@ -95,7 +95,7 @@ void noSuchActivityType() throws IOException, InterruptedException { @Test void validationNotice() throws IOException, InterruptedException { - final var activityId = hasura.insertActivity( + final var activityId = hasura.insertActivityDirective( planId, "BiteBanana", "1h", @@ -110,7 +110,7 @@ void validationNotice() throws IOException, InterruptedException { @Test void instantiationError() throws IOException, InterruptedException { - final var activityId = hasura.insertActivity( + final var activityId = hasura.insertActivityDirective( planId, "BakeBananaBread", "1h", @@ -133,7 +133,7 @@ void instantiationError() throws IOException, InterruptedException { @Test void noSuchMissionModelError() throws IOException, InterruptedException { - final var activityId = hasura.insertActivity( + final var activityId = hasura.insertActivityDirective( planId, "BiteBanana", "1h", @@ -157,7 +157,7 @@ void noSuchMissionModelError() throws IOException, InterruptedException { @Test void exceptionDuringValidationHandled() throws IOException, InterruptedException { - final var exceptionActivityId = hasura.insertActivity( + final var exceptionActivityId = hasura.insertActivityDirective( planId, "ExceptionActivity", "1h", @@ -166,7 +166,7 @@ void exceptionDuringValidationHandled() throws IOException, InterruptedException // sleep to make sure exception activity is picked up Thread.sleep(1000); // TODO consider a while loop here - final var biteActivityId = hasura.insertActivity( + final var biteActivityId = hasura.insertActivityDirective( planId, "BiteBanana", "1h", diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/ConstraintsTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/ConstraintsTests.java index d0438908db..50aa3728f6 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/ConstraintsTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/ConstraintsTests.java @@ -68,7 +68,7 @@ void beforeEach() throws IOException, InterruptedException { "1212h", "2021-01-01T00:00:00Z"); //Insert the Activity - activityId = hasura.insertActivity( + activityId = hasura.insertActivityDirective( planId, "BiteBanana", "1h", @@ -143,7 +143,7 @@ void constraintsSucceedAgainstVariantWithUnits() throws IOException { planId, "export default (): Constraint => Real.Resource(\"/fruit\").equal(3)", ""); - hasura.insertActivity( + hasura.insertActivityDirective( planId, "PeelBanana", "1h", @@ -264,7 +264,7 @@ void constraintsWorkMonthLongActivity() throws IOException { "export default (): Constraint => Windows.During(ActivityType.ControllableDurationActivity).not()"); hasura.deleteActivity(planId, activityId); final long thirtyFiveDays = 35 * 24 * 60 * 60 * 1000L * 1000; // 35 days in microseconds - hasura.insertActivity( + hasura.insertActivityDirective( planId, "ControllableDurationActivity", "0h", diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java index 478fb5c661..5f1a81c0fc 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java @@ -139,23 +139,26 @@ void afterEach() throws IOException { hasura.deleteMissionModel(modelId); } + private int directiveId1 = -1; + private int directiveId2 = -1; + private void insertActivities() throws IOException { // Duration argument is specified on one but not the other to verify that the scheduler can pick up on effective args - hasura.insertActivity(planId, "GrowBanana", "1h", JsonValue.EMPTY_JSON_OBJECT); - hasura.insertActivity(planId, "GrowBanana", "3h", Json.createObjectBuilder() - .add("growingDuration", 7200000000L) // 2h - .build()); + directiveId1 = hasura.insertActivityDirective(planId, "GrowBanana", "1h", JsonValue.EMPTY_JSON_OBJECT); + directiveId2 = hasura.insertActivityDirective(planId, "GrowBanana", "3h", Json.createObjectBuilder() + .add("growingDuration", 7200000000L) // 2h + .build()); hasura.updatePlanRevisionSchedulingSpec(planId); } private void insertSatisfyingActivities() throws IOException { // Duration argument is specified on one but not the other to verify that the scheduler can pick up on effective args - hasura.insertActivity(planId, "BiteBanana", "2h5m", Json.createObjectBuilder() - .add("biteSize", 1) // 2h - .build()); - hasura.insertActivity(planId, "BiteBanana", "5h5m", Json.createObjectBuilder() - .add("biteSize", 1) // 2h - .build()); + hasura.insertActivityDirective(planId, "BiteBanana", "2h5m", Json.createObjectBuilder() + .add("biteSize", 1) // 2h + .build()); + hasura.insertActivityDirective(planId, "BiteBanana", "5h5m", Json.createObjectBuilder() + .add("biteSize", 1) // 2h + .build()); hasura.updatePlanRevisionSchedulingSpec(planId); } @@ -163,18 +166,18 @@ private ArrayList insertAnchorActivities() throws IOException { ArrayList anchors = new ArrayList(); // Duration argument is specified on one but not the other to verify that the scheduler can pick up on effective args - Integer id1 = hasura.insertActivity(planId, "GrowBanana", "0h", - Json.createObjectBuilder() + Integer id1 = hasura.insertActivityDirective(planId, "GrowBanana", "0h", + Json.createObjectBuilder() .add("growingDuration", 10800000L) // 3h .build()); anchors.add(id1); - Integer id2 = hasura.insertActivity(planId, "GrowBanana", "5h", - Json.createObjectBuilder() + Integer id2 = hasura.insertActivityDirective(planId, "GrowBanana", "5h", + Json.createObjectBuilder() .add("growingDuration", 10800000L) // 3h .build()); anchors.add(id1); - Integer id3 = hasura.insertActivity(planId, "GrowBanana", "10h", - Json.createObjectBuilder() + Integer id3 = hasura.insertActivityDirective(planId, "GrowBanana", "10h", + Json.createObjectBuilder() .add("growingDuration", 10800000L) // 3h .build()); anchors.add(id3); @@ -501,7 +504,7 @@ public void beforeEach() throws IOException { void outdatedPlanRevision() throws IOException { hasura.awaitSimulation(planId); // Add Grow Banana - hasura.insertActivity(planId, "GrowBanana", "5h", JsonObject.EMPTY_JSON_OBJECT); + hasura.insertActivityDirective(planId, "GrowBanana", "5h", JsonObject.EMPTY_JSON_OBJECT); // Setup: Add Goal final int coexistenceGoalId = hasura.createSchedulingSpecGoal( @@ -601,6 +604,30 @@ void injectedResultsLoaded() throws IOException{ plantType, List.of(new ProfileSegment("0h", false, Json.createValue(400)))); + hasura.insertActivityInstance( + datasetId, + directiveId1, + "GrowBanana", + "1h", + "1h", + Json.createObjectBuilder() + .add("quantity", 1) + .add("growingDuration", 3600000000L) + .build() + ); + + hasura.insertActivityInstance( + datasetId, + directiveId2, + "GrowBanana", + "3h", + "2h", + Json.createObjectBuilder() + .add("quantity", 1) + .add("growingDuration", 7200000000L) + .build() + ); + // Insert Goal final int plantGoal = hasura.createSchedulingSpecGoal( "Scheduling Test: When Plant < 300", diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SimulationTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SimulationTests.java index d509cd36e2..048e3d57f2 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SimulationTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SimulationTests.java @@ -88,7 +88,7 @@ void afterEach() throws IOException { @Test void simulationMicrosecondResolution() throws IOException { final var activityArgs = Json.createObjectBuilder().add("duration", 1).build(); - hasura.insertActivity(planId, "ControllableDurationActivity", "1h", activityArgs); + hasura.insertActivityDirective(planId, "ControllableDurationActivity", "1h", activityArgs); assertDoesNotThrow(() -> hasura.awaitSimulation(planId)); } @@ -97,8 +97,8 @@ void simulationMicrosecondResolution() throws IOException { */ @Test void incompleteParentIdsPosted() throws IOException { - hasura.insertActivity(planId, "parent", "0h", JsonValue.EMPTY_JSON_OBJECT); - hasura.insertActivity(planId, "DecomposingSpawnParent", "0h", JsonValue.EMPTY_JSON_OBJECT); + hasura.insertActivityDirective(planId, "parent", "0h", JsonValue.EMPTY_JSON_OBJECT); + hasura.insertActivityDirective(planId, "DecomposingSpawnParent", "0h", JsonValue.EMPTY_JSON_OBJECT); final var simulatedActivities = hasura.getSimulationDataset(hasura.awaitSimulation(planId).simDatasetId()) .activities(); @@ -150,17 +150,17 @@ class TemporalSubsetSimulation { @BeforeEach void beforeEach() throws IOException { // Insert Activities - firstHalfActivityId = hasura.insertActivity( + firstHalfActivityId = hasura.insertActivityDirective( planId, "ControllableDurationActivity", "1hr", Json.createObjectBuilder().add("duration", 3600000000L).build()); //1hr duration - midpointActivityId = hasura.insertActivity( + midpointActivityId = hasura.insertActivityDirective( planId, "ControllableDurationActivity", "12hr", Json.createObjectBuilder().add("duration", 7200000000L).build()); //2hr duration - secondHalfActivityId = hasura.insertActivity( + secondHalfActivityId = hasura.insertActivityDirective( planId, "ControllableDurationActivity", "14hr", @@ -324,7 +324,7 @@ void beforeEach() throws IOException, InterruptedException { .add("duration", Json.createObjectBuilder() .add("amountInMicroseconds", 60000000*60L)) // 1 hour .build(); - hasura.insertActivity(fooPlan, "ControllableDurationActivity", startOffset, growArgs); + hasura.insertActivityDirective(fooPlan, "ControllableDurationActivity", startOffset, growArgs); } } @@ -451,7 +451,7 @@ void afterEach() throws IOException { @Test void directiveIdIncluded() throws IOException { // Setup: Insert a directive that will throw - int dirId = hasura.insertActivity( + int dirId = hasura.insertActivityDirective( fooPlan, "DaemonCheckerActivity", "01:00:00", @@ -499,7 +499,7 @@ void directiveIdIncluded() throws IOException { @Test void directiveIdOnDescendant() throws IOException { // Setup: Insert a directive that will throw - int dirId = hasura.insertActivity( + int dirId = hasura.insertActivityDirective( fooPlan, "DaemonTaskActivity", "01:00:00", @@ -593,7 +593,7 @@ void noDirectiveIdDaemonMidDirective() throws IOException { // Set up: Update the config to force an exception 1hr into the plan // and add an activity that will be executing at that time hasura.updateSimArguments(fooPlan, Json.createObjectBuilder().add("raiseException", JsonValue.TRUE).build()); - hasura.insertActivity( + hasura.insertActivityDirective( fooPlan, "DaemonCheckerSpawner", "00:59:00", diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java index 75713a8d38..b025dcd112 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java @@ -92,7 +92,7 @@ void beforeEach() throws IOException, InterruptedException { "1212h", "2021-01-01T00:00:00Z"); //Insert the Activity - activityId = hasura.insertActivity( + activityId = hasura.insertActivityDirective( planId, "BiteBanana", "1h", diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java index 9f7df289d0..71a81e6ea6 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java @@ -17,8 +17,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.OptionalLong; import java.util.function.Function; import java.util.stream.Collectors; @@ -221,7 +219,7 @@ public void deletePlan(int planId) throws IOException { makeRequest(GQL.DELETE_PLAN, variables); } - public int insertActivity(int planId, String type, String startOffset, JsonObject arguments) throws IOException { + public int insertActivityDirective(int planId, String type, String startOffset, JsonObject arguments) throws IOException { final var insertActivityBuilder = Json.createObjectBuilder() .add("plan_id", planId) .add("type", type) @@ -231,6 +229,26 @@ public int insertActivity(int planId, String type, String startOffset, JsonObjec return makeRequest(GQL.CREATE_ACTIVITY_DIRECTIVE, variables).getJsonObject("createActivityDirective").getInt("id"); } + public void insertActivityInstance(int datasetId, int directiveId, String type, String startOffset, String duration, JsonObject arguments) throws IOException { + final var hasuraAdminHeader = Map.of("x-hasura-role", "admin"); + + final var insertActivityBuilder = Json.createObjectBuilder() + .add("span_id", directiveId) + .add("dataset_id", datasetId) + .add("type", type) + .add("start_offset", startOffset) + .add("duration", duration) + .add( + "attributes", + Json.createObjectBuilder() + .add("arguments", arguments) + .add("directiveId", directiveId) + .add("computedAttributes", Json.createObjectBuilder()) + ); + final var variables = Json.createObjectBuilder().add("span", insertActivityBuilder).build(); + makeRequest(GQL.INSERT_SPAN, variables, hasuraAdminHeader); + } + public void updateActivityDirectiveArguments(int planId, int activityId, JsonObject arguments) throws IOException { final var variables = Json.createObjectBuilder() .add("plan_id", planId) From 4305a70553842f533fcce3c38961ace25ed52702 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Tue, 3 Sep 2024 17:03:12 -0700 Subject: [PATCH 050/108] Small timeline QOL updates --- .../fooprocedures/procedures/SimulationDemo.java | 2 +- .../examples/fooprocedures/procedures/StayWellFed.java | 9 ++++----- .../nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt | 5 ++++- .../gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt | 4 ++-- .../nasa/ammos/aerie/procedural/timeline/plan/Plan.kt | 3 +++ 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java index c619a01b02..7fbed12fc2 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java @@ -35,7 +35,7 @@ public void run(EditablePlan plan) { final var connections = lowFruit.starts().shift(Duration.MINUTE.negate()) .connectTo(bites.ends(), false); - for (final var connection: connections.collect()) { + for (final var connection: connections) { assert connection.to != null; plan.create( "GrowBanana", diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java index c01806953f..aa42e74816 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java @@ -50,8 +50,8 @@ public void run(@NotNull final EditablePlan plan) { // This goal will only apply during the Dole mission phase. :) final var dolePhase = simResults .resource("/producer", Strings::deserialize) - .highlightEqualTo("Dole"); - dolePhase.cache(); + .highlightEqualTo("Dole") + .cache(); // Manual recurrence goal: during Dole phase, require a bitebanana every `bitePeriod`. final var bites = plan.directives("BiteBanana") @@ -59,7 +59,7 @@ public void run(@NotNull final EditablePlan plan) { .collect(); var currentTime = Duration.MIN_VALUE; - for (final var phase: dolePhase.collect()) { + for (final var phase: dolePhase) { currentTime = Duration.max(currentTime, phase.start); while (currentTime.plus(bitePeriod).shorterThan(phase.end)) { @@ -91,8 +91,7 @@ public void run(@NotNull final EditablePlan plan) { // So we iteratively find the first time /fruit drops below zero // and add a grow banana fix it. We then mock the effect of grow banana // by adding one to /fruit, rather than resimulating, and do it again. - var fruit = simResults.resource("/fruit", Real::deserialize); - fruit.cache(); + var fruit = simResults.resource("/fruit", Real::deserialize).cache(); var ranOutAt = fruit.lessThan(0).filterByWindows(dolePhase, true).risingEdges().highlightTrue().collect(); while (!ranOutAt.isEmpty()) { diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt index 5f2fc9bf38..fc76784872 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/BaseTimeline.kt @@ -15,13 +15,16 @@ data class BaseTimeline, TL: Timeline>( private var cached: List? = null private var cachedOptions: CollectOptions? = null - override fun cache(opts: CollectOptions) { + override fun cache(opts: CollectOptions): TL { if (cachedOptions == null || !cachedOptions!!.contains(opts)) { cached = collect(opts) cachedOptions = opts } + return specialize() } + override fun iterator(): Iterator = collect().iterator() + override fun collect(opts: CollectOptions) = if (cached == null || !cachedOptions!!.contains(opts)) collector(opts) else ctor(BaseTimeline(ctor, listCollector(cached!!))).collect(opts) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt index 2178095f2f..7cb22d57ca 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/Timeline.kt @@ -8,7 +8,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike * Should be implemented by composing a raw timeline in your container * and delegating to it with the `by` keyword. See any timeline for examples. */ -interface Timeline, THIS: Timeline> { +interface Timeline, THIS: Timeline>: Iterable { /** * [(DOC)][collect] Evaluates the stack of operations and produces a list of timeline payload objects. * @@ -62,7 +62,7 @@ interface Timeline, THIS: Timeline> { /** * Caches the result of collecting this timeline, to be reused for future collect requests if possible. */ - fun cache(opts: CollectOptions) + fun cache(opts: CollectOptions): THIS /** * [(DOC)][cache] A simplified version of [cache]. diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/Plan.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/Plan.kt index 4ef6ef1b50..5efc8401ca 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/Plan.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/Plan.kt @@ -12,6 +12,9 @@ interface Plan { /** Total extent of the plan's bounds, whether it was simulated on the full extent or not. */ fun totalBounds(): Interval + /** The total duration of the plan, whether simulated on the full extent or not. */ + fun duration() = totalBounds().duration() + /** Convert a time instant to a relative duration (relative to plan start). */ fun toRelative(abs: Instant): Duration /** Convert a relative duration to a time instant. */ From f32a88973c70ab29a57dd6c6c03117864a50b440 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 4 Sep 2024 12:44:00 -0700 Subject: [PATCH 051/108] Rebase after stateless aerie --- .../nasa/jpl/aerie/constraints/model/ActivityInstance.java | 2 +- e2e-tests/build.gradle | 1 + .../java/gov/nasa/jpl/aerie/merlin/driver/ActivityId.java | 4 ---- .../nasa/jpl/aerie/merlin/driver/SimulationResults.java | 3 ++- .../nasa/jpl/aerie/merlin/driver/UnfinishedActivity.java | 1 + .../jpl/aerie/merlin/driver/engine/SimulationEngine.java | 4 ++-- .../nasa/jpl/aerie/merlin/driver/AnchorSimulationTest.java | 2 ++ .../aerie/merlin/driver/TemporalSubsetSimulationTests.java | 2 ++ .../jpl/aerie/merlin/server/http/ResponseSerializers.java | 2 +- .../merlin/server/models/SimulationResultsHandle.java | 4 ++-- .../server/remotes/InMemoryResultsCellRepository.java | 4 ++-- .../remotes/postgres/PostgresResultsCellRepository.java | 4 ++-- .../orchestration/simulation/SimulationResultsWriter.java | 4 ++-- procedural/constraints/build.gradle | 1 + .../nasa/ammos/aerie/procedural/constraints/Violation.kt | 2 +- .../nasa/ammos/aerie/procedural/constraints/Violations.kt | 2 +- procedural/examples/foo-procedures/build.gradle | 2 +- procedural/remote/build.gradle | 1 + .../ammos/aerie/procedural/remote/AeriePostgresPlan.kt | 2 +- .../procedural/remote/AeriePostgresSimulationResults.kt | 5 ++--- procedural/scheduling/build.gradle | 1 + .../ammos/aerie/procedural/scheduling/plan/EditablePlan.kt | 2 +- .../ammos/aerie/procedural/scheduling/plan/NewDirective.kt | 2 +- procedural/timeline/build.gradle | 1 + .../procedural/timeline/payloads/activities/Directive.kt | 2 +- .../timeline/payloads/activities/DirectiveStart.kt | 2 +- .../procedural/timeline/payloads/activities/Instance.kt | 7 +++---- .../aerie/scheduler/simulation/SimulationFacadeUtils.java | 4 ++-- .../scheduler/simulation/SimulationResultsConverter.java | 2 +- .../nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt | 6 +----- .../plan/MerlinToProcedureSimulationResultsAdapter.kt | 6 +++--- .../aerie/scheduler/simulation/AnchorSchedulerTest.java | 4 ++-- .../simulation/SimulationResultsComparisonUtils.java | 2 +- .../server/services/GraphQLMerlinDatabaseService.java | 4 ++-- .../worker/services/SynchronousSchedulerAgent.java | 1 - .../src/main/java/gov/nasa/jpl/aerie/types/ActivityId.java | 4 ++++ .../java/gov/nasa/jpl/aerie/types}/ActivityInstance.java | 3 +-- .../java/gov/nasa/jpl/aerie/types}/ActivityInstanceId.java | 2 +- 38 files changed, 55 insertions(+), 52 deletions(-) delete mode 100644 merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityId.java create mode 100644 type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityId.java rename {merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver => type-utils/src/main/java/gov/nasa/jpl/aerie/types}/ActivityInstance.java (84%) rename {merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver => type-utils/src/main/java/gov/nasa/jpl/aerie/types}/ActivityInstanceId.java (61%) diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/ActivityInstance.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/ActivityInstance.java index 8a3c8758c3..4296b52fb8 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/ActivityInstance.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/ActivityInstance.java @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.constraints.model; import gov.nasa.jpl.aerie.constraints.time.Interval; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; import gov.nasa.jpl.aerie.types.ActivityDirectiveId; diff --git a/e2e-tests/build.gradle b/e2e-tests/build.gradle index 5c2ec884aa..4ff48fdab6 100644 --- a/e2e-tests/build.gradle +++ b/e2e-tests/build.gradle @@ -59,6 +59,7 @@ dependencies { testImplementation "com.zaxxer:HikariCP:5.1.0" testImplementation("org.postgresql:postgresql:42.6.0") testImplementation project(':merlin-driver') + testImplementation project(':type-utils') testImplementation 'com.microsoft.playwright:playwright:1.37.0' diff --git a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityId.java b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityId.java deleted file mode 100644 index fe5ee3d894..0000000000 --- a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityId.java +++ /dev/null @@ -1,4 +0,0 @@ -package gov.nasa.jpl.aerie.merlin.driver; - -public interface ActivityId { -} diff --git a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/SimulationResults.java b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/SimulationResults.java index 8f3e5f481d..3cc0b6431d 100644 --- a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/SimulationResults.java +++ b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/SimulationResults.java @@ -7,7 +7,8 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.RealDynamics; import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; -import org.apache.commons.lang3.tuple.Pair; +import gov.nasa.jpl.aerie.types.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import org.apache.commons.lang3.tuple.Triple; import java.time.Instant; diff --git a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/UnfinishedActivity.java b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/UnfinishedActivity.java index b6ef8f4a60..a8e4279162 100644 --- a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/UnfinishedActivity.java +++ b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/UnfinishedActivity.java @@ -2,6 +2,7 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; import gov.nasa.jpl.aerie.types.ActivityDirectiveId; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import java.time.Instant; import java.util.List; diff --git a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/engine/SimulationEngine.java b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/engine/SimulationEngine.java index f1000b8bdd..916df89c95 100644 --- a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/engine/SimulationEngine.java +++ b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/engine/SimulationEngine.java @@ -1,8 +1,8 @@ package gov.nasa.jpl.aerie.merlin.driver.engine; import gov.nasa.jpl.aerie.merlin.driver.MissionModel.SerializableTopic; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstance; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId; +import gov.nasa.jpl.aerie.types.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import gov.nasa.jpl.aerie.merlin.driver.resources.SimulationResourceManager; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; import gov.nasa.jpl.aerie.merlin.driver.UnfinishedActivity; diff --git a/merlin-driver/src/test/java/gov/nasa/jpl/aerie/merlin/driver/AnchorSimulationTest.java b/merlin-driver/src/test/java/gov/nasa/jpl/aerie/merlin/driver/AnchorSimulationTest.java index 2e87d5e944..9a73431d2e 100644 --- a/merlin-driver/src/test/java/gov/nasa/jpl/aerie/merlin/driver/AnchorSimulationTest.java +++ b/merlin-driver/src/test/java/gov/nasa/jpl/aerie/merlin/driver/AnchorSimulationTest.java @@ -4,6 +4,8 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; import gov.nasa.jpl.aerie.types.ActivityDirective; import gov.nasa.jpl.aerie.types.ActivityDirectiveId; +import gov.nasa.jpl.aerie.types.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import gov.nasa.jpl.aerie.types.SerializedActivity; import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.DisplayName; diff --git a/merlin-driver/src/test/java/gov/nasa/jpl/aerie/merlin/driver/TemporalSubsetSimulationTests.java b/merlin-driver/src/test/java/gov/nasa/jpl/aerie/merlin/driver/TemporalSubsetSimulationTests.java index cd67abdd32..a2c2eabb58 100644 --- a/merlin-driver/src/test/java/gov/nasa/jpl/aerie/merlin/driver/TemporalSubsetSimulationTests.java +++ b/merlin-driver/src/test/java/gov/nasa/jpl/aerie/merlin/driver/TemporalSubsetSimulationTests.java @@ -4,6 +4,8 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; import gov.nasa.jpl.aerie.types.ActivityDirective; import gov.nasa.jpl.aerie.types.ActivityDirectiveId; +import gov.nasa.jpl.aerie.types.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import gov.nasa.jpl.aerie.types.SerializedActivity; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/ResponseSerializers.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/ResponseSerializers.java index a32f70e568..a47e503e6c 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/ResponseSerializers.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/ResponseSerializers.java @@ -5,7 +5,7 @@ import gov.nasa.jpl.aerie.constraints.model.ConstraintResult; import gov.nasa.jpl.aerie.constraints.time.Interval; import gov.nasa.jpl.aerie.json.JsonParseResult.FailureReason; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstance; import gov.nasa.jpl.aerie.merlin.driver.UnfinishedActivity; import gov.nasa.jpl.aerie.merlin.driver.json.ValueSchemaJsonParser; import gov.nasa.jpl.aerie.merlin.protocol.model.InputType.Parameter; diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/models/SimulationResultsHandle.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/models/SimulationResultsHandle.java index 8a94cec3dd..a097ae7ab3 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/models/SimulationResultsHandle.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/models/SimulationResultsHandle.java @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.merlin.server.models; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstance; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId; +import gov.nasa.jpl.aerie.types.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/InMemoryResultsCellRepository.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/InMemoryResultsCellRepository.java index 5e60a356ef..0524fa9d58 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/InMemoryResultsCellRepository.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/InMemoryResultsCellRepository.java @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.merlin.server.remotes; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstance; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId; +import gov.nasa.jpl.aerie.types.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import gov.nasa.jpl.aerie.merlin.driver.SimulationFailure; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; import gov.nasa.jpl.aerie.merlin.driver.resources.ResourceProfile; diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresResultsCellRepository.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresResultsCellRepository.java index 39b7cd2f5e..51de431da5 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresResultsCellRepository.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresResultsCellRepository.java @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.merlin.server.remotes.postgres; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstance; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId; +import gov.nasa.jpl.aerie.types.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import gov.nasa.jpl.aerie.merlin.driver.SimulationException; import gov.nasa.jpl.aerie.merlin.driver.SimulationFailure; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; diff --git a/orchestration-utils/src/main/java/gov/nasa/jpl/aerie/orchestration/simulation/SimulationResultsWriter.java b/orchestration-utils/src/main/java/gov/nasa/jpl/aerie/orchestration/simulation/SimulationResultsWriter.java index 9b4fab105b..043d965e36 100644 --- a/orchestration-utils/src/main/java/gov/nasa/jpl/aerie/orchestration/simulation/SimulationResultsWriter.java +++ b/orchestration-utils/src/main/java/gov/nasa/jpl/aerie/orchestration/simulation/SimulationResultsWriter.java @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.orchestration.simulation; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstance; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId; +import gov.nasa.jpl.aerie.types.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; import gov.nasa.jpl.aerie.merlin.driver.UnfinishedActivity; import gov.nasa.jpl.aerie.merlin.driver.engine.EventRecord; diff --git a/procedural/constraints/build.gradle b/procedural/constraints/build.gradle index 87e8f93561..5d149d08b2 100644 --- a/procedural/constraints/build.gradle +++ b/procedural/constraints/build.gradle @@ -15,6 +15,7 @@ repositories { dependencies { implementation project(':procedural:timeline') implementation project(':merlin-driver') + implementation project(':type-utils') testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violation.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violation.kt index b3fb5c7cfc..3fcbc3e9cd 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violation.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violation.kt @@ -2,7 +2,7 @@ package gov.nasa.ammos.aerie.procedural.constraints import gov.nasa.ammos.aerie.procedural.timeline.Interval import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike -import gov.nasa.jpl.aerie.merlin.driver.ActivityId +import gov.nasa.jpl.aerie.types.ActivityId /** A single violation of a constraint. */ data class Violation( diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt index 831c2b4a1e..3a5c894cd6 100644 --- a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt +++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/Violations.kt @@ -12,7 +12,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Instance import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList -import gov.nasa.jpl.aerie.merlin.driver.ActivityId +import gov.nasa.jpl.aerie.types.ActivityId /** A timeline of [Violations][Violation]. */ data class Violations(private val timeline: Timeline): diff --git a/procedural/examples/foo-procedures/build.gradle b/procedural/examples/foo-procedures/build.gradle index eedf525530..3ba155b189 100644 --- a/procedural/examples/foo-procedures/build.gradle +++ b/procedural/examples/foo-procedures/build.gradle @@ -20,8 +20,8 @@ dependencies { implementation project(':procedural:constraints') implementation project(':procedural:scheduling') implementation project(':procedural:timeline') - implementation project(':merlin-framework') implementation project(':merlin-driver') + implementation project(':type-utils') implementation project(':contrib') testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' diff --git a/procedural/remote/build.gradle b/procedural/remote/build.gradle index d54145aea9..82b13ab83f 100644 --- a/procedural/remote/build.gradle +++ b/procedural/remote/build.gradle @@ -18,6 +18,7 @@ dependencies { implementation project(':merlin-driver') implementation project(':scheduler-driver') implementation project(':parsing-utilities') + implementation project(':type-utils') testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' diff --git a/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresPlan.kt b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresPlan.kt index f520f28052..05f7701f0e 100644 --- a/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresPlan.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresPlan.kt @@ -12,7 +12,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveSta import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan import gov.nasa.ammos.aerie.procedural.timeline.util.duration.plus import gov.nasa.ammos.aerie.procedural.timeline.util.duration.minus -import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId +import gov.nasa.jpl.aerie.types.ActivityDirectiveId import java.io.StringReader import java.sql.Connection import java.time.Instant diff --git a/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt index cb65cac928..a7b1f94de9 100644 --- a/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt @@ -12,8 +12,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Instance import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults -import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId +import gov.nasa.jpl.aerie.types.ActivityDirectiveId import java.io.StringReader import java.sql.Connection import javax.json.Json @@ -166,7 +165,7 @@ data class AeriePostgresSimulationResults( result.add(Instance( deserializer(attributes), response.getString(4), - ActivityInstanceId(id), + gov.nasa.jpl.aerie.types.ActivityInstanceId(id), directiveId?.let { ActivityDirectiveId(it) }, between(start, start.plus(Duration.parseISO8601(response.getString(2)))) )) diff --git a/procedural/scheduling/build.gradle b/procedural/scheduling/build.gradle index de5cef2745..4377e02fe8 100644 --- a/procedural/scheduling/build.gradle +++ b/procedural/scheduling/build.gradle @@ -16,6 +16,7 @@ repositories { dependencies { implementation project(':procedural:timeline') implementation project(':merlin-driver') + implementation project(':type-utils') testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/EditablePlan.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/EditablePlan.kt index 9d8837729a..5a6e87b638 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/EditablePlan.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/EditablePlan.kt @@ -6,7 +6,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults -import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId +import gov.nasa.jpl.aerie.types.ActivityDirectiveId /** A plan representation that can be edited and simulated. */ interface EditablePlan: Plan { diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt index e81dbe4aae..35320eeb43 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/plan/NewDirective.kt @@ -3,7 +3,7 @@ package gov.nasa.ammos.aerie.procedural.scheduling.plan import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart -import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId +import gov.nasa.jpl.aerie.types.ActivityDirectiveId /** A new directive to be created, which doesn't have an id yet. */ data class NewDirective( diff --git a/procedural/timeline/build.gradle b/procedural/timeline/build.gradle index 934b797369..743f1479b8 100644 --- a/procedural/timeline/build.gradle +++ b/procedural/timeline/build.gradle @@ -15,6 +15,7 @@ repositories { dependencies { implementation project(':merlin-driver') + implementation project(':type-utils') testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt index 12fc360bab..f0a3a9d01d 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Directive.kt @@ -2,7 +2,7 @@ package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.ammos.aerie.procedural.timeline.Interval -import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId +import gov.nasa.jpl.aerie.types.ActivityDirectiveId /** A wrapper of any type of activity directive containing common data. */ data class Directive( diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/DirectiveStart.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/DirectiveStart.kt index 973f0aff3b..c799ab36a4 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/DirectiveStart.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/DirectiveStart.kt @@ -1,7 +1,7 @@ package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities -import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.types.ActivityDirectiveId /** Start behavior for an activity directive. */ sealed interface DirectiveStart { diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt index 09cee6a470..932e916693 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt @@ -1,9 +1,8 @@ package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities -import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.ammos.aerie.procedural.timeline.Interval -import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.types.ActivityDirectiveId /** A wrapper of any type of activity instance containing common data. */ data class Instance( @@ -12,7 +11,7 @@ data class Instance( override val type: String, /** The instance id. */ - @JvmField val id: ActivityInstanceId, + @JvmField val id: gov.nasa.jpl.aerie.types.ActivityInstanceId, /** * The maybe-null id of the directive associated with this instance. diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationFacadeUtils.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationFacadeUtils.java index 772172fae5..377dd09300 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationFacadeUtils.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationFacadeUtils.java @@ -1,7 +1,7 @@ package gov.nasa.jpl.aerie.scheduler.simulation; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstance; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId; +import gov.nasa.jpl.aerie.types.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import gov.nasa.jpl.aerie.merlin.driver.SimulationResultsComputerInputs; import gov.nasa.jpl.aerie.merlin.driver.engine.SimulationEngine; import gov.nasa.jpl.aerie.merlin.protocol.model.SchedulerModel; diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationResultsConverter.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationResultsConverter.java index b858e31a8b..1d027b7642 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationResultsConverter.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationResultsConverter.java @@ -4,7 +4,7 @@ import gov.nasa.jpl.aerie.constraints.model.DiscreteProfile; import gov.nasa.jpl.aerie.constraints.model.LinearProfile; import gov.nasa.jpl.aerie.constraints.time.Interval; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstance; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt index a90c9b9abd..26591b71c6 100644 --- a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt @@ -2,25 +2,21 @@ package gov.nasa.jpl.aerie.scheduler.plan import gov.nasa.jpl.aerie.merlin.driver.MissionModel import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue import gov.nasa.ammos.aerie.procedural.scheduling.plan.Edit import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan import gov.nasa.ammos.aerie.procedural.scheduling.plan.NewDirective import gov.nasa.ammos.aerie.procedural.scheduling.simulation.SimulateOptions import gov.nasa.jpl.aerie.scheduler.simulation.SimulationFacade -import gov.nasa.ammos.aerie.procedural.timeline.collections.Directives import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan -import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId import gov.nasa.jpl.aerie.merlin.protocol.types.DurationType import gov.nasa.jpl.aerie.scheduler.DirectiveIdGenerator import gov.nasa.jpl.aerie.scheduler.model.* -import gov.nasa.jpl.aerie.scheduler.plan.InMemoryEditablePlan.Companion.validateArguments +import gov.nasa.jpl.aerie.types.ActivityDirectiveId import java.time.Instant import kotlin.jvm.optionals.getOrNull -import kotlin.math.absoluteValue import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults as TimelineSimResults data class InMemoryEditablePlan( diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt index ccbc56b0a1..7ccf27c528 100644 --- a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt @@ -1,6 +1,5 @@ package gov.nasa.jpl.aerie.scheduler.plan -import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId import gov.nasa.jpl.aerie.merlin.driver.engine.ProfileSegment import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue @@ -12,7 +11,8 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Instance import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId +import gov.nasa.jpl.aerie.types.ActivityDirectiveId +import gov.nasa.jpl.aerie.types.ActivityInstanceId import java.time.Instant import kotlin.jvm.optionals.getOrNull @@ -108,7 +108,7 @@ class MerlinToProcedureSimulationResultsAdapter( instances.add(Instance( deserializer(serializedActivity), a.type, - ActivityInstanceId(a.spanId), + ActivityInstanceId(a.spanId), a.directiveId?.let { ActivityDirectiveId(it) }, Interval(startTime, endTime) )) diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/AnchorSchedulerTest.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/AnchorSchedulerTest.java index b09ef69b90..452b25e6a5 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/AnchorSchedulerTest.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/AnchorSchedulerTest.java @@ -5,8 +5,8 @@ import gov.nasa.jpl.aerie.merlin.driver.DirectiveTypeRegistry; import gov.nasa.jpl.aerie.merlin.driver.MissionModel; import gov.nasa.jpl.aerie.merlin.driver.OneStepTask; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstance; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId; +import gov.nasa.jpl.aerie.types.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import gov.nasa.jpl.aerie.merlin.driver.SimulationEngineConfiguration; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; import gov.nasa.jpl.aerie.merlin.driver.SimulationResultsComputerInputs; diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationResultsComparisonUtils.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationResultsComparisonUtils.java index 227e93b89e..3e4a983042 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationResultsComparisonUtils.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationResultsComparisonUtils.java @@ -1,6 +1,6 @@ package gov.nasa.jpl.aerie.scheduler.simulation; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstance; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; import gov.nasa.jpl.aerie.merlin.driver.engine.ProfileSegment; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java index e0801bb649..d3b9998302 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java @@ -4,8 +4,8 @@ import gov.nasa.jpl.aerie.constraints.model.LinearProfile; import gov.nasa.jpl.aerie.json.BasicParsers; import gov.nasa.jpl.aerie.json.JsonParser; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstance; -import gov.nasa.jpl.aerie.merlin.driver.ActivityInstanceId; +import gov.nasa.jpl.aerie.types.ActivityInstance; +import gov.nasa.jpl.aerie.types.ActivityInstanceId; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; import gov.nasa.jpl.aerie.merlin.driver.UnfinishedActivity; import gov.nasa.jpl.aerie.merlin.driver.engine.EventRecord; diff --git a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java index e73b4635b0..d8b793d0eb 100644 --- a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java +++ b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java @@ -22,7 +22,6 @@ import java.util.stream.Collectors; import gov.nasa.jpl.aerie.json.JsonParser; -import gov.nasa.jpl.aerie.merlin.driver.ActivityDirectiveId; import gov.nasa.jpl.aerie.merlin.driver.MissionModel; import gov.nasa.jpl.aerie.merlin.driver.MissionModelLoader; import gov.nasa.jpl.aerie.merlin.driver.SimulationEngineConfiguration; diff --git a/type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityId.java b/type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityId.java new file mode 100644 index 0000000000..3d03863377 --- /dev/null +++ b/type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityId.java @@ -0,0 +1,4 @@ +package gov.nasa.jpl.aerie.types; + +public interface ActivityId { +} diff --git a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityInstance.java b/type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityInstance.java similarity index 84% rename from merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityInstance.java rename to type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityInstance.java index 4fa9e27b24..36922a0589 100644 --- a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityInstance.java +++ b/type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityInstance.java @@ -1,8 +1,7 @@ -package gov.nasa.jpl.aerie.merlin.driver; +package gov.nasa.jpl.aerie.types; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; -import gov.nasa.jpl.aerie.types.ActivityDirectiveId; import java.time.Instant; import java.util.List; diff --git a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityInstanceId.java b/type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityInstanceId.java similarity index 61% rename from merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityInstanceId.java rename to type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityInstanceId.java index eeb44b6a6c..2075be0415 100644 --- a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/ActivityInstanceId.java +++ b/type-utils/src/main/java/gov/nasa/jpl/aerie/types/ActivityInstanceId.java @@ -1,3 +1,3 @@ -package gov.nasa.jpl.aerie.merlin.driver; +package gov.nasa.jpl.aerie.types; public record ActivityInstanceId(long id) implements ActivityId {} From 96af26e2579ae9c6f8e8898ae0ca83ffc053b0bd Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 4 Sep 2024 13:04:26 -0700 Subject: [PATCH 052/108] Add parentId field to instances --- .../remote/AeriePostgresSimulationResults.kt | 17 ++++--- .../timeline/payloads/activities/Instance.kt | 6 ++- ...rlinToProcedureSimulationResultsAdapter.kt | 49 ++++++++++--------- 3 files changed, 41 insertions(+), 31 deletions(-) diff --git a/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt index a7b1f94de9..d5be02a5bf 100644 --- a/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt +++ b/procedural/remote/src/main/kotlin/gov/nasa/ammos/aerie/procedural/remote/AeriePostgresSimulationResults.kt @@ -13,6 +13,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Instance import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults import gov.nasa.jpl.aerie.types.ActivityDirectiveId +import gov.nasa.jpl.aerie.types.ActivityInstanceId import java.io.StringReader import java.sql.Connection import javax.json.Json @@ -141,11 +142,11 @@ data class AeriePostgresSimulationResults( } private val allInstancesStatement = c.prepareStatement( - "select start_offset, duration, attributes, activity_type_name, id from merlin.simulated_activity" + + "select start_offset, duration, attributes, activity_type_name, id, parent_id from merlin.simulated_activity" + " where simulation_dataset_id = ?;" ) private val filteredInstancesStatement = c.prepareStatement( - "select start_offset, duration, attributes, activity_type_name, id from merlin.simulated_activity" + + "select start_offset, duration, attributes, activity_type_name, id, parent_id from merlin.simulated_activity" + " where simulation_dataset_id = ? and activity_type_name = ?;" ) override fun instances(type: String?, deserializer: (SerializedValue) -> A): Instances { @@ -159,15 +160,17 @@ data class AeriePostgresSimulationResults( while (response.next()) { val start = Duration.parseISO8601(response.getString(1)) val id = response.getLong(5) + val parentId = response.getLong(6) val attributesString = response.getString(3) val attributes = parseJson(attributesString) val directiveId = attributes.asMap().getOrNull()?.get("directiveId")?.asInt()?.getOrNull() result.add(Instance( - deserializer(attributes), - response.getString(4), - gov.nasa.jpl.aerie.types.ActivityInstanceId(id), - directiveId?.let { ActivityDirectiveId(it) }, - between(start, start.plus(Duration.parseISO8601(response.getString(2)))) + deserializer(attributes), + response.getString(4), + ActivityInstanceId(id), + directiveId?.let { ActivityDirectiveId(it) }, + if (parentId == 0L) null else ActivityInstanceId(parentId), + between(start, start.plus(Duration.parseISO8601(response.getString(2)))) )) } return Instances(result) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt index 932e916693..f213dfe7d8 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt @@ -3,6 +3,7 @@ package gov.nasa.ammos.aerie.procedural.timeline.payloads.activities import gov.nasa.ammos.aerie.procedural.timeline.Interval import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.jpl.aerie.types.ActivityDirectiveId +import gov.nasa.jpl.aerie.types.ActivityInstanceId /** A wrapper of any type of activity instance containing common data. */ data class Instance( @@ -11,7 +12,7 @@ data class Instance( override val type: String, /** The instance id. */ - @JvmField val id: gov.nasa.jpl.aerie.types.ActivityInstanceId, + @JvmField val id: ActivityInstanceId, /** * The maybe-null id of the directive associated with this instance. @@ -19,10 +20,11 @@ data class Instance( * Will be `null` if this is a child activity. */ @JvmField val directiveId: ActivityDirectiveId?, + @JvmField val parentId: ActivityInstanceId?, override val interval: Interval, ): Activity> { override val startTime: Duration get() = interval.start - override fun withNewInterval(i: Interval) = Instance(inner, type, id, directiveId, i) + override fun withNewInterval(i: Interval) = Instance(inner, type, id, directiveId, parentId, i) } diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt index 7ccf27c528..c3da7cc682 100644 --- a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/MerlinToProcedureSimulationResultsAdapter.kt @@ -8,6 +8,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.collections.Instances import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceSegmentsOp import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Activity import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Instance import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults @@ -62,9 +63,10 @@ class MerlinToProcedureSimulationResultsAdapter( private data class CommonActivity( val arguments: Map, val type: String, - val directiveId: Long?, - val spanId: Long, + val directiveId: ActivityDirectiveId?, + val instanceId: ActivityInstanceId, val startTime: Instant, + val parentId: ActivityInstanceId?, val finishedActivityAttributes: FinishedActivityAttributes? ) @@ -72,22 +74,24 @@ class MerlinToProcedureSimulationResultsAdapter( val result = mutableListOf() for ((key, a) in results.simulatedActivities) { result.add(CommonActivity( - a.arguments, - a.type, - a.directiveId.map(ActivityDirectiveId::id).getOrNull(), - key.id, - a.start, - FinishedActivityAttributes(a.duration, a.computedAttributes) + a.arguments, + a.type, + a.directiveId.getOrNull(), + ActivityInstanceId(key.id), + a.start, + a.parentId, + FinishedActivityAttributes(a.duration, a.computedAttributes) )) } for ((key, a) in results.unfinishedActivities) { result.add(CommonActivity( - a.arguments, - a.type, - a.directiveId.map(ActivityDirectiveId::id).getOrNull(), - key.id, - a.start, - null + a.arguments, + a.type, + a.directiveId.getOrNull(), + ActivityInstanceId(key.id), + a.start, + a.parentId, + null )) } result @@ -99,18 +103,19 @@ class MerlinToProcedureSimulationResultsAdapter( if (type != null && a.type != type) continue val startTime = plan.toRelative(a.startTime) val endTime = a.finishedActivityAttributes?.let { it.duration + startTime } - ?: simBounds().end + ?: simBounds().end val computedAttributes = a.finishedActivityAttributes?.computedAttributes ?: SerializedValue.of(mapOf()) val serializedActivity = SerializedValue.of(mapOf( - "arguments" to SerializedValue.of(a.arguments), - "computedAttributes" to computedAttributes + "arguments" to SerializedValue.of(a.arguments), + "computedAttributes" to computedAttributes )) instances.add(Instance( - deserializer(serializedActivity), - a.type, - ActivityInstanceId(a.spanId), - a.directiveId?.let { ActivityDirectiveId(it) }, - Interval(startTime, endTime) + deserializer(serializedActivity), + a.type, + a.instanceId, + a.directiveId, + a.parentId, + Interval(startTime, endTime) )) } return Instances(instances) From d87027fdd9b585cc3ec4e5d425d4d6537dcf29d5 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Wed, 4 Sep 2024 12:14:48 -0700 Subject: [PATCH 053/108] remove procedural-scheduling comment from goal_invocations migration --- .../hasura/migrations/Aerie/10_goal_invocations/down.sql | 3 --- 1 file changed, 3 deletions(-) diff --git a/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql b/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql index 5d021f4f30..16e5b63037 100644 --- a/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql +++ b/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql @@ -87,7 +87,4 @@ alter table scheduler.scheduling_specification_goals drop column goal_invocation_id; -comment on column scheduler.scheduling_goal_definition.definition is e'' - 'An executable expression in the Merlin scheduling language.'; - call migrations.mark_migration_rolled_back('10'); From f63138ed01600973b8e516463a7df0d32dc32b1a Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Wed, 4 Sep 2024 12:15:05 -0700 Subject: [PATCH 054/108] explictly mark columns as non-null before PKing --- .../hasura/migrations/Aerie/10_goal_invocations/down.sql | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql b/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql index 16e5b63037..ef235a20f1 100644 --- a/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql +++ b/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql @@ -27,6 +27,15 @@ set goal_id = ga.goal_id, goal_revision = ga.goal_revision from scheduler.scheduling_goal_analysis ga where (ca.analysis_id, ca.goal_invocation_id) = (ga.analysis_id, ga.goal_invocation_id); +-- explictly restore non-nullability before PKing +alter table scheduler.scheduling_goal_analysis_satisfying_activities + alter column goal_id integer set not null, + alter column goal_revision integer set not null; + +alter table scheduler.scheduling_goal_analysis_created_activities + alter column goal_id integer set not null, + alter column goal_revision integer set not null; + alter table scheduler.scheduling_goal_analysis_satisfying_activities add constraint satisfying_activities_primary_key primary key (analysis_id, goal_id, goal_revision, activity_id), From 76bf92b1b88376e6c7b11ec06b0078e76aa2cc34 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Wed, 4 Sep 2024 12:17:11 -0700 Subject: [PATCH 055/108] use FK config mapping instead of manual config --- .../scheduling_run/scheduling_goal_analysis.yaml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml index 43c12ae278..950c06974f 100644 --- a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml +++ b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml @@ -23,13 +23,9 @@ object_relationships: array_relationships: - name: satisfying_activities using: - manual_configuration: - column_mapping: - goal_invocation_id: goal_invocation_id - analysis_id: analysis_id - remote_table: - name: scheduling_goal_analysis_satisfying_activities - schema: scheduler + foreign_key_constraint_on: + - goal_invocation_id + - analysis_id select_permissions: - role: aerie_admin permission: From 2199df4b907d95774d07d38548e8a1e90fb840fb Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Wed, 4 Sep 2024 12:29:27 -0700 Subject: [PATCH 056/108] add more db-tests to check goal definition type constraint --- .../database/SchedulerDatabaseTests.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/db-tests/src/test/java/gov/nasa/jpl/aerie/database/SchedulerDatabaseTests.java b/db-tests/src/test/java/gov/nasa/jpl/aerie/database/SchedulerDatabaseTests.java index fc16d71b83..d0ff727c32 100644 --- a/db-tests/src/test/java/gov/nasa/jpl/aerie/database/SchedulerDatabaseTests.java +++ b/db-tests/src/test/java/gov/nasa/jpl/aerie/database/SchedulerDatabaseTests.java @@ -612,5 +612,91 @@ void testCantPartiallyChangeEDSLGoalToProcedure() throws SQLException { ); } } + + @Test + void testCantHaveEDSLGoalWithoutDefinition() throws SQLException { + try (final var statement = connection.createStatement()) { + final var exception = assertThrowsExactly( + PSQLException.class, + () -> statement.executeUpdate( + //language=sql + """ + update scheduler.scheduling_goal_definition + set definition = null + where goal_id = %d + """.formatted(goalIds[0]) + ) + ); + + assertTrue(exception.getMessage().contains( + "new row for relation \"scheduling_goal_definition\" violates check constraint \"check_goal_definition_type_consistency\"") + ); + } + } + + @Test + void testCantHaveEDSLGoalWithUploadedJarID() throws SQLException { + try (final var statement = connection.createStatement()) { + final var jarId = merlinHelper.insertFileUpload(); + + final var exception = assertThrowsExactly( + PSQLException.class, + () -> statement.executeUpdate( + //language=sql + """ + update scheduler.scheduling_goal_definition + set uploaded_jar_id = %d + where goal_id = %d + """.formatted(jarId, goalIds[0]) + ) + ); + + assertTrue(exception.getMessage().contains( + "new row for relation \"scheduling_goal_definition\" violates check constraint \"check_goal_definition_type_consistency\"") + ); + } + } + + @Test + void testCantHaveProcedureWithDefinition() throws SQLException { + try (final var statement = connection.createStatement()) { + final var exception = assertThrowsExactly( + PSQLException.class, + () -> statement.executeUpdate( + //language=sql + """ + update scheduler.scheduling_goal_definition + set definition = 'nope' + where goal_id = %d + """.formatted(goalIds[1]) + ) + ); + + assertTrue(exception.getMessage().contains( + "new row for relation \"scheduling_goal_definition\" violates check constraint \"check_goal_definition_type_consistency\"") + ); + } + } + + @Test + void testCantHaveProcedureWithoutUploadedJarId() throws SQLException { + try (final var statement = connection.createStatement()) { + final var exception = assertThrowsExactly( + PSQLException.class, + () -> statement.executeUpdate( + //language=sql + """ + update scheduler.scheduling_goal_definition + set uploaded_jar_id = null + where goal_id = %d + """.formatted(goalIds[1]) + ) + ); + + assertTrue(exception.getMessage().contains( + "new row for relation \"scheduling_goal_definition\" violates check constraint \"check_goal_definition_type_consistency\"") + ); + } + } } } From 6b19cbff36ecd62fdbef0486e0cedde9ccb0ff68 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Wed, 4 Sep 2024 12:53:32 -0700 Subject: [PATCH 057/108] remove update permissions for event trigger --- .../tables/scheduler/scheduling_goal_definition.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml index d7392d6f75..882da9524a 100644 --- a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml +++ b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_goal_definition.yaml @@ -98,11 +98,6 @@ event_triggers: enable_manual: false insert: columns: "*" - update: - columns: - - goal_id - - revision - - uploaded_jar_id name: refreshSchedulingProcedureParameterTypes retry_conf: interval_sec: 10 From a4c891465ff68f4f547ed8aafe1cc5f22c9bf37f Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Wed, 4 Sep 2024 12:56:04 -0700 Subject: [PATCH 058/108] add new explicit `set not null` statements --- .../migrations/Aerie/10_goal_invocations/up.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql b/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql index 027e93724c..d0dbdf0bc9 100644 --- a/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql +++ b/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql @@ -25,6 +25,10 @@ from scheduler.scheduling_request as sr where sr.analysis_id = sga.analysis_id and sga.goal_id = ssg.goal_id; +-- explictly set not null before PKing +alter table scheduler.scheduling_goal_analysis + alter column goal_invocation_id set not null; + alter table scheduler.scheduling_goal_analysis add constraint scheduling_goal_analysis_primary_key primary key (analysis_id, goal_invocation_id); @@ -46,6 +50,10 @@ from scheduler.scheduling_request as sr where sr.analysis_id = sgaca.analysis_id and sgaca.goal_id = ssg.goal_id; +-- explictly set not null before PKing +alter table scheduler.scheduling_goal_analysis_created_activities + alter column goal_invocation_id set not null; + alter table scheduler.scheduling_goal_analysis_created_activities drop column goal_id, drop column goal_revision, @@ -75,6 +83,10 @@ from scheduler.scheduling_request as sr where sr.analysis_id = sgasa.analysis_id and sgasa.goal_id = ssg.goal_id; +-- explictly set not null before PKing +alter table scheduler.scheduling_goal_analysis_satisfying_activities + alter column goal_invocation_id set not null; + alter table scheduler.scheduling_goal_analysis_satisfying_activities drop column goal_id, drop column goal_revision, From 06c12c801896b6f63d086c10a454e01f4b16399d Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Wed, 4 Sep 2024 13:06:07 -0700 Subject: [PATCH 059/108] update comments in migration --- .../Aerie/10_goal_invocations/down.sql | 16 ++++++++++++++++ .../migrations/Aerie/10_goal_invocations/up.sql | 3 +++ 2 files changed, 19 insertions(+) diff --git a/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql b/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql index ef235a20f1..2c61377f11 100644 --- a/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql +++ b/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql @@ -10,6 +10,11 @@ alter table scheduler.scheduling_goal_analysis_satisfying_activities add column goal_id integer null, add column goal_revision integer null; +comment on column scheduler.scheduling_goal_analysis_satisfying_activities.goal_id is e'' + 'The associated goal ID.'; +comment on column scheduler.scheduling_goal_analysis_satisfying_activities.goal_revision is e'' + 'The associated version of the goal definition used.'; + alter table scheduler.scheduling_goal_analysis_created_activities drop constraint created_activities_primary_key, drop constraint created_activities_references_scheduling_goal_analysis, @@ -17,6 +22,11 @@ alter table scheduler.scheduling_goal_analysis_created_activities add column goal_id integer null, add column goal_revision integer null; +comment on column scheduler.scheduling_goal_analysis_created_activities.goal_id is e'' + 'The associated goal ID.'; +comment on column scheduler.scheduling_goal_analysis_created_activities.goal_revision is e'' + 'The associated version of the goal definition used.'; + update scheduler.scheduling_goal_analysis_satisfying_activities as sa set goal_id = ga.goal_id, goal_revision = ga.goal_revision from scheduler.scheduling_goal_analysis ga @@ -96,4 +106,10 @@ alter table scheduler.scheduling_specification_goals drop column goal_invocation_id; +comment on column scheduler.scheduling_specification_goals.specification_id is e'' + 'The plan scheduling specification this goal is on. Half of the primary key.'; + +comment on column scheduler.scheduling_specification_goals.goal_id is e'' + 'The id of a specific goal in the specification. Half of the primary key.'; + call migrations.mark_migration_rolled_back('10'); diff --git a/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql b/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql index d0dbdf0bc9..6eda21fb58 100644 --- a/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql +++ b/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql @@ -14,6 +14,9 @@ alter table scheduler.scheduling_goal_analysis drop constraint scheduling_goal_analysis_primary_key; +comment on table scheduler.scheduling_goal_analysis is e'' + 'The analysis of single goal invocation from a scheduling run.'; + comment on column scheduler.scheduling_goal_analysis.goal_invocation_id is e'' 'The associated goal invocation ID.'; From 6802a24b26444a91484dfd6a8d88d37a8d404ac5 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Wed, 4 Sep 2024 13:33:33 -0700 Subject: [PATCH 060/108] merge proc sched migrations into one --- .../Aerie/10_goal_invocations/down.sql | 34 +++++++++++++ .../Aerie/10_goal_invocations/up.sql | 50 +++++++++++++++++++ .../Aerie/11_procedural_scheduling/down.sql | 35 ------------- .../Aerie/11_procedural_scheduling/up.sql | 50 ------------------- .../sql/applied_migrations.sql | 1 - 5 files changed, 84 insertions(+), 86 deletions(-) delete mode 100644 deployment/hasura/migrations/Aerie/11_procedural_scheduling/down.sql delete mode 100644 deployment/hasura/migrations/Aerie/11_procedural_scheduling/up.sql diff --git a/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql b/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql index 2c61377f11..7586dfe2bf 100644 --- a/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql +++ b/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql @@ -1,3 +1,37 @@ +-- delete all scheduling procedures from specs +-- (on delete is restricted) +delete from scheduler.scheduling_specification_goals sg + using scheduler.scheduling_goal_definition gd + where gd.goal_id = sg.goal_id + and gd.type = 'JAR'::scheduler.goal_type; + +-- delete all scheduling procedure definitions +delete from scheduler.scheduling_goal_metadata gm + using scheduler.scheduling_goal_definition gd + where gm.id = gd.goal_id + and gd.type = 'JAR'::scheduler.goal_type; + +alter table scheduler.scheduling_goal_analysis + drop column arguments; + +alter table scheduler.scheduling_specification_goals + drop column arguments; + +alter table scheduler.scheduling_goal_definition + drop constraint check_goal_definition_type_consistency, + drop constraint scheduling_procedure_has_uploaded_jar, + + drop column type, + drop column uploaded_jar_id, + drop column parameter_schema, + + alter column definition set not null; + +comment on column scheduler.scheduling_goal_definition.definition is e'' + 'An executable expression in the Merlin scheduling language.'; + +drop type scheduler.goal_type; + -- restore goal_analysis_* tables -- drop new PKs for created / satisfying activities diff --git a/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql b/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql index 6eda21fb58..79b1b531b6 100644 --- a/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql +++ b/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql @@ -102,4 +102,54 @@ alter table scheduler.scheduling_goal_analysis_satisfying_activities on update cascade on delete cascade; +-- begin procedural work +create type scheduler.goal_type as enum ('EDSL', 'JAR'); + +alter table scheduler.scheduling_goal_definition + add column type scheduler.goal_type not null default 'EDSL', + add column uploaded_jar_id integer, + add column parameter_schema jsonb, + + alter column definition drop not null, + + add constraint scheduling_procedure_has_uploaded_jar + foreign key (uploaded_jar_id) + references merlin.uploaded_file + on update cascade + on delete restrict, + + add constraint check_goal_definition_type_consistency + check ( + (type = 'EDSL' and definition is not null and uploaded_jar_id is null) + or + (type = 'JAR' and uploaded_jar_id is not null and definition is null) + ); + +comment on column scheduler.scheduling_goal_definition.type is e'' + 'The type of this goal definition, "EDSL" or "JAR".'; +comment on column scheduler.scheduling_goal_definition.definition is e'' + 'An executable expression in the Merlin scheduling language.' + 'Should be non-null when type is EDSL'; +comment on column scheduler.scheduling_goal_definition.uploaded_jar_id is e'' + 'The foreign key to the uploaded_file entry containing the procedure jar' + 'Should be non-null when type is JAR'; +comment on column scheduler.scheduling_goal_definition.parameter_schema is e'' + 'The schema for parameters that can be passed to goal instances using this definition.' + 'Similar schema to parameter_set''s in Merlin.'; + +alter table scheduler.scheduling_specification_goals + add column arguments jsonb not null default '{}'::jsonb; + +comment on column scheduler.scheduling_specification_goals.arguments is e'' + 'The arguments that will be passed to this goal when invoked.' + 'Follows scheduler.scheduling_goal_definition.parameter_schema.' + 'Only valid for procedural goals.'; + +alter table scheduler.scheduling_goal_analysis + add column arguments jsonb not null default '{}'::jsonb; + +comment on column scheduler.scheduling_goal_analysis.arguments is e'' + 'The "as run" arguments passed to this goal during the scheduling run.' + 'Follows scheduler.scheduling_goal_definition.parameter_schema.'; + call migrations.mark_migration_applied('10'); diff --git a/deployment/hasura/migrations/Aerie/11_procedural_scheduling/down.sql b/deployment/hasura/migrations/Aerie/11_procedural_scheduling/down.sql deleted file mode 100644 index e685118169..0000000000 --- a/deployment/hasura/migrations/Aerie/11_procedural_scheduling/down.sql +++ /dev/null @@ -1,35 +0,0 @@ --- delete all scheduling procedures from specs --- (on delete is restricted) -delete from scheduler.scheduling_specification_goals sg - using scheduler.scheduling_goal_definition gd - where gd.goal_id = sg.goal_id - and gd.type = 'JAR'::scheduler.goal_type; - --- delete all scheduling procedure definitions -delete from scheduler.scheduling_goal_metadata gm - using scheduler.scheduling_goal_definition gd - where gm.id = gd.goal_id - and gd.type = 'JAR'::scheduler.goal_type; - -alter table scheduler.scheduling_goal_analysis - drop column arguments; - -alter table scheduler.scheduling_specification_goals - drop column arguments; - -alter table scheduler.scheduling_goal_definition - drop constraint check_goal_definition_type_consistency, - drop constraint scheduling_procedure_has_uploaded_jar, - - drop column type, - drop column uploaded_jar_id, - drop column parameter_schema, - - alter column definition set not null; - -comment on column scheduler.scheduling_goal_definition.definition is e'' - 'An executable expression in the Merlin scheduling language.'; - -drop type scheduler.goal_type; - -call migrations.mark_migration_rolled_back('11'); diff --git a/deployment/hasura/migrations/Aerie/11_procedural_scheduling/up.sql b/deployment/hasura/migrations/Aerie/11_procedural_scheduling/up.sql deleted file mode 100644 index 1198e06b0b..0000000000 --- a/deployment/hasura/migrations/Aerie/11_procedural_scheduling/up.sql +++ /dev/null @@ -1,50 +0,0 @@ -create type scheduler.goal_type as enum ('EDSL', 'JAR'); - -alter table scheduler.scheduling_goal_definition - add column type scheduler.goal_type not null default 'EDSL', - add column uploaded_jar_id integer, - add column parameter_schema jsonb, - - alter column definition drop not null, - - add constraint scheduling_procedure_has_uploaded_jar - foreign key (uploaded_jar_id) - references merlin.uploaded_file - on update cascade - on delete restrict, - - add constraint check_goal_definition_type_consistency - check ( - (type = 'EDSL' and definition is not null and uploaded_jar_id is null) - or - (type = 'JAR' and uploaded_jar_id is not null and definition is null) - ); - -comment on column scheduler.scheduling_goal_definition.type is e'' - 'The type of this goal definition, "EDSL" or "JAR".'; -comment on column scheduler.scheduling_goal_definition.definition is e'' - 'An executable expression in the Merlin scheduling language.' - 'Should be non-null when type is EDSL'; -comment on column scheduler.scheduling_goal_definition.uploaded_jar_id is e'' - 'The foreign key to the uploaded_file entry containing the procedure jar' - 'Should be non-null when type is JAR'; -comment on column scheduler.scheduling_goal_definition.parameter_schema is e'' - 'The schema for parameters that can be passed to goal instances using this definition.' - 'Similar schema to parameter_set''s in Merlin.'; - -alter table scheduler.scheduling_specification_goals - add column arguments jsonb not null default '{}'::jsonb; - -comment on column scheduler.scheduling_specification_goals.arguments is e'' - 'The arguments that will be passed to this goal when invoked.' - 'Follows scheduler.scheduling_goal_definition.parameter_schema.' - 'Only valid for procedural goals.'; - -alter table scheduler.scheduling_goal_analysis - add column arguments jsonb not null default '{}'::jsonb; - -comment on column scheduler.scheduling_goal_analysis.arguments is e'' - 'The "as run" arguments passed to this goal during the scheduling run.' - 'Follows scheduler.scheduling_goal_definition.parameter_schema.'; - -call migrations.mark_migration_applied('11'); diff --git a/deployment/postgres-init-db/sql/applied_migrations.sql b/deployment/postgres-init-db/sql/applied_migrations.sql index 9f1d007a71..245dc51deb 100644 --- a/deployment/postgres-init-db/sql/applied_migrations.sql +++ b/deployment/postgres-init-db/sql/applied_migrations.sql @@ -13,4 +13,3 @@ call migrations.mark_migration_applied('7'); call migrations.mark_migration_applied('8'); call migrations.mark_migration_applied('9'); call migrations.mark_migration_applied('10'); -call migrations.mark_migration_applied('11'); From a9150f41da8f164bc8bc742705b01fb42e90724e Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Wed, 4 Sep 2024 13:35:11 -0700 Subject: [PATCH 061/108] rename 10th migration --- .../{10_goal_invocations => 10_procedural_scheduling}/down.sql | 0 .../{10_goal_invocations => 10_procedural_scheduling}/up.sql | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename deployment/hasura/migrations/Aerie/{10_goal_invocations => 10_procedural_scheduling}/down.sql (100%) rename deployment/hasura/migrations/Aerie/{10_goal_invocations => 10_procedural_scheduling}/up.sql (100%) diff --git a/deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql similarity index 100% rename from deployment/hasura/migrations/Aerie/10_goal_invocations/down.sql rename to deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql diff --git a/deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql similarity index 100% rename from deployment/hasura/migrations/Aerie/10_goal_invocations/up.sql rename to deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql From 43bf7d4a1272e44922115f66e5e28e1322491014 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Wed, 4 Sep 2024 14:14:34 -0700 Subject: [PATCH 062/108] fix sql syntax --- .../migrations/Aerie/10_procedural_scheduling/down.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql index 7586dfe2bf..4956634b65 100644 --- a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql +++ b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql @@ -73,12 +73,12 @@ where (ca.analysis_id, ca.goal_invocation_id) = (ga.analysis_id, ga.goal_invocat -- explictly restore non-nullability before PKing alter table scheduler.scheduling_goal_analysis_satisfying_activities - alter column goal_id integer set not null, - alter column goal_revision integer set not null; + alter column goal_id set not null, + alter column goal_revision set not null; alter table scheduler.scheduling_goal_analysis_created_activities - alter column goal_id integer set not null, - alter column goal_revision integer set not null; + alter column goal_id set not null, + alter column goal_revision set not null; alter table scheduler.scheduling_goal_analysis_satisfying_activities add constraint satisfying_activities_primary_key From 0dff7595be633f8156ef2f35b1399f96aa552061 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 4 Sep 2024 14:49:36 -0700 Subject: [PATCH 063/108] Better deserializer for constants --- .../timeline/collections/profiles/Constants.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Constants.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Constants.kt index 827f18887f..dc728f2414 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Constants.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Constants.kt @@ -6,13 +6,12 @@ import gov.nasa.ammos.aerie.procedural.timeline.BaseTimeline import gov.nasa.ammos.aerie.procedural.timeline.Interval import gov.nasa.ammos.aerie.procedural.timeline.Timeline import gov.nasa.ammos.aerie.procedural.timeline.ops.SerialConstantOps -import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceSegmentsOp import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList /** A profile of piece-wise constant values. */ data class Constants(private val timeline: Timeline, Constants>): - Timeline, Constants> by timeline, - SerialConstantOps> + Timeline, Constants> by timeline, + SerialConstantOps> { constructor(v: V): this(Segment(Interval.MIN_MAX, v)) constructor(vararg segments: Segment): this(segments.asList()) @@ -20,11 +19,11 @@ data class Constants(private val timeline: Timeline, Constant /***/ companion object { /** - * Delegates to the constructor, for use with [gov.nasa.ammos.aerie.procedural.timeline.plan.Plan.resource]. - * - * Does not convert the serialized values because it does not know what it should be converted into. - * You will need to do that yourself using [mapValues]. + * Returns a deserializer, for use with [gov.nasa.ammos.aerie.procedural.timeline.plan.Plan.resource]. */ - @JvmStatic fun deserialize(list: List>) = Constants(list) + @JvmStatic fun deserialize(mapper: (Segment) -> V): (List>) -> Constants = { + l: List> -> + Constants(l.map { segment -> segment.mapValue(mapper) }) + } } } From c6751c57a7d89045fe41251881b4326f5bd5f30b Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Wed, 4 Sep 2024 14:58:07 -0700 Subject: [PATCH 064/108] fix foreign key array relationship metadata --- .../scheduling_run/scheduling_goal_analysis.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml index 950c06974f..5642b0dc8f 100644 --- a/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml +++ b/deployment/hasura/metadata/databases/tables/scheduler/scheduling_run/scheduling_goal_analysis.yaml @@ -24,8 +24,12 @@ array_relationships: - name: satisfying_activities using: foreign_key_constraint_on: - - goal_invocation_id - - analysis_id + columns: + - goal_invocation_id + - analysis_id + table: + name: scheduling_goal_analysis_satisfying_activities + schema: scheduler select_permissions: - role: aerie_admin permission: From b7f0a313a3d72fe54f68f5a1c17f7c2ffe65ab6a Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 4 Sep 2024 17:51:48 -0700 Subject: [PATCH 065/108] Small rework for deserializers --- .../gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java | 2 +- .../examples/fooprocedures/constraints/ConstFruit.java | 5 +++-- .../fooprocedures/procedures/SimulationDemo.java | 2 +- .../examples/fooprocedures/procedures/StayWellFed.java | 4 ++-- .../timeline/collections/profiles/Booleans.kt | 4 +++- .../timeline/collections/profiles/Constants.kt | 2 +- .../timeline/collections/profiles/Numbers.kt | 4 ++-- .../procedural/timeline/collections/profiles/Real.kt | 4 ++-- .../timeline/collections/profiles/Strings.kt | 10 ++++++---- .../timeline/payloads/activities/AnyDirective.kt | 2 +- .../timeline/payloads/activities/AnyInstance.kt | 4 ++-- .../nasa/ammos/aerie/procedural/timeline/plan/Plan.kt | 4 ++-- .../procedural/timeline/plan/SimulationResults.kt | 4 ++-- 13 files changed, 28 insertions(+), 23 deletions(-) diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java index b025dcd112..d53d30cecd 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/TimelineRemoteTests.java @@ -136,7 +136,7 @@ void queryActivityDirectives() { @Test void queryResources() { - final var fruit = simulatedPlan.resource("/fruit", Real::deserialize).collect(); + final var fruit = simulatedPlan.resource("/fruit", Real.deserializer()).collect(); assertIterableEquals( List.of( Segment.of(Interval.betweenClosedOpen(Duration.ZERO, Duration.HOUR), new LinearEquation(4.0)), diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java index c934c367bd..9a2e7b9f93 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java @@ -2,8 +2,8 @@ import gov.nasa.ammos.aerie.procedural.constraints.Violations; import gov.nasa.ammos.aerie.procedural.constraints.Constraint; -import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real; import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions; +import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real; import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulatedPlan; import org.jetbrains.annotations.NotNull; @@ -11,7 +11,8 @@ public class ConstFruit implements Constraint { @NotNull @Override public Violations run(SimulatedPlan plan, @NotNull CollectOptions options) { - final var fruit = plan.resource("/fruit", Real::deserialize); + final var fruit = plan.resource("/fruit", Real.deserializer()); + return Violations.violateOn( fruit.equalTo(4), diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java index 7fbed12fc2..bbceb3bde0 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java @@ -29,7 +29,7 @@ public void run(EditablePlan plan) { var simResults = plan.latestResults(); if (simResults == null) simResults = plan.simulate(); - final var lowFruit = simResults.resource("/fruit", Real::deserialize).lessThan(3.5).isolateTrue(); + final var lowFruit = simResults.resource("/fruit", Real.deserializer()).lessThan(3.5).isolateTrue(); final var bites = simResults.instances("BiteBanana"); final var connections = lowFruit.starts().shift(Duration.MINUTE.negate()) diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java index aa42e74816..c51d303948 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/StayWellFed.java @@ -49,7 +49,7 @@ public void run(@NotNull final EditablePlan plan) { // I'm using producer as a substitute for a mission phase variable. // This goal will only apply during the Dole mission phase. :) final var dolePhase = simResults - .resource("/producer", Strings::deserialize) + .resource("/producer", Strings.deserializer()) .highlightEqualTo("Dole") .cache(); @@ -91,7 +91,7 @@ public void run(@NotNull final EditablePlan plan) { // So we iteratively find the first time /fruit drops below zero // and add a grow banana fix it. We then mock the effect of grow banana // by adding one to /fruit, rather than resimulating, and do it again. - var fruit = simResults.resource("/fruit", Real::deserialize).cache(); + var fruit = simResults.resource("/fruit", Real.deserializer()).cache(); var ranOutAt = fruit.lessThan(0).filterByWindows(dolePhase, true).risingEdges().highlightTrue().collect(); while (!ranOutAt.isEmpty()) { diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Booleans.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Booleans.kt index 709549bddf..b92443ce93 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Booleans.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Booleans.kt @@ -119,6 +119,8 @@ data class Booleans(private val timeline: Timeline, Booleans>): * Converts a list of serialized value segments into a [Booleans] profile; * for use with [gov.nasa.ammos.aerie.procedural.timeline.plan.Plan.resource]. */ - @JvmStatic fun deserialize(list: List>) = Booleans(list.map { it.withNewValue(it.value.asBoolean().get()) }) + @JvmStatic fun deserializer() = { list: List> -> + Booleans(list.map { it.withNewValue(it.value.asBoolean().get()) }) + } } } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Constants.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Constants.kt index dc728f2414..22ba1a50a9 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Constants.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Constants.kt @@ -21,7 +21,7 @@ data class Constants(private val timeline: Timeline, Constant /** * Returns a deserializer, for use with [gov.nasa.ammos.aerie.procedural.timeline.plan.Plan.resource]. */ - @JvmStatic fun deserialize(mapper: (Segment) -> V): (List>) -> Constants = { + @JvmStatic fun deserializer(mapper: (Segment) -> V): (List>) -> Constants = { l: List> -> Constants(l.map { segment -> segment.mapValue(mapper) }) } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Numbers.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Numbers.kt index 3de00c0085..128aed4569 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Numbers.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Numbers.kt @@ -214,7 +214,7 @@ data class Numbers(private val timeline: Timeline, Numbers * * Prefers converting to longs if possible, and falls back to doubles if not. */ - @JvmStatic fun deserialize(list: List>) = Numbers(list.map { seg -> + @JvmStatic fun deserializer() = { list: List> -> Numbers(list.map { seg -> val bigDecimal = seg.value.asNumeric().orElseThrow { Exception("value was not numeric: $seg") } val number: Number = try { bigDecimal.longValueExact() @@ -222,6 +222,6 @@ data class Numbers(private val timeline: Timeline, Numbers bigDecimal.toDouble() } seg.withNewValue(number) - }) + }) } } } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt index 8f06967158..58321aaba8 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Real.kt @@ -210,7 +210,7 @@ data class Real(private val timeline: Timeline, Real>): * While plain numbers are acceptable and will be converted to a [LinearEquation] without warning, consider using [Numbers] * to keep the precision. */ - @JvmStatic fun deserialize(list: List>): Real { + @JvmStatic fun deserializer() = { list: List> -> val converted: List> = list.map { s -> s.value.asReal().getOrNull()?.let { return@map s.withNewValue(LinearEquation(it)) } val map = s.value.asMap().orElseThrow { RealDeserializeException("value was not a map or plain number: $s") } @@ -221,7 +221,7 @@ data class Real(private val timeline: Timeline, Real>): .asReal().orElseThrow { RealDeserializeException("rate was not a double") } s.withNewValue(LinearEquation(s.interval.start, initialValue, rate)) } - return Real(converted) + Real(converted) } /***/ class RealDeserializeException(message: String): Exception(message) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Strings.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Strings.kt index 78ce7840c6..6a110f2db5 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Strings.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/profiles/Strings.kt @@ -52,9 +52,11 @@ data class Strings(private val timeline: Timeline, Strings>): * Converts a list of serialized value segments into a [Strings] profile; * for use with [gov.nasa.ammos.aerie.procedural.timeline.plan.Plan.resource]. */ - @JvmStatic fun deserialize(list: List>) = Strings(list.map { seg -> - val string = seg.value.asString().orElseThrow { Exception("value was not a string: $seg") } - seg.withNewValue(string) - }) + @JvmStatic fun deserializer() = { list: List> -> + Strings(list.map { seg -> + val string = seg.value.asString().orElseThrow { Exception("value was not a string: $seg") } + seg.withNewValue(string) + }) + } } } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyDirective.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyDirective.kt index a5b13a1d4d..331a3f4358 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyDirective.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyDirective.kt @@ -11,6 +11,6 @@ data class AnyDirective( fun serialize(): SerializedValue = SerializedValue.of(arguments) /***/ companion object { /** Converts a [SerializedValue] object containing activity arguments into an [AnyDirective] object. */ - @JvmStatic fun deserialize(attributes: SerializedValue) = AnyDirective(attributes.asMap().getOrNull()!!) + @JvmStatic fun deserializer() = { attributes: SerializedValue -> AnyDirective(attributes.asMap().getOrNull()!!) } } } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyInstance.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyInstance.kt index d91d7f1ed2..b056da5473 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyInstance.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/AnyInstance.kt @@ -12,14 +12,14 @@ data class AnyInstance( /** * Converts a [SerializedValue] object containing activity arguments and computed attributes to an [AnyInstance] object. */ - fun deserialize(attributes: SerializedValue): AnyInstance { + fun deserializer() = { attributes: SerializedValue -> /***/ class InstanceDeserializeError(message: String): Error(message) val arguments = attributes.asMap().getOrNull()!!["arguments"]?.asMap()?.getOrNull() ?: throw InstanceDeserializeError("Could not get arguments from attributes: $attributes") val computedAttributes = attributes.asMap().getOrNull()!!["computedAttributes"] ?: throw InstanceDeserializeError("Could not get computed attributes from attributes: $attributes") - return AnyInstance(arguments, computedAttributes) + AnyInstance(arguments, computedAttributes) } } } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/Plan.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/Plan.kt index 5efc8401ca..b120212c21 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/Plan.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/Plan.kt @@ -28,7 +28,7 @@ interface Plan { */ fun directives(type: String?, deserializer: (SerializedValue) -> A): Directives /** Queries activity directives, filtered by type, deserializing them as [AnyDirective]. **/ - fun directives(type: String) = directives(type, AnyDirective::deserialize) + fun directives(type: String) = directives(type, AnyDirective.deserializer()) /** Queries all activity directives, deserializing them as [AnyDirective]. **/ - fun directives() = directives(null, AnyDirective::deserialize) + fun directives() = directives(null, AnyDirective.deserializer()) } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/SimulationResults.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/SimulationResults.kt index 561ebcb628..2ddcb70190 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/SimulationResults.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/SimulationResults.kt @@ -31,7 +31,7 @@ interface SimulationResults { */ fun instances(type: String?, deserializer: (SerializedValue) -> A): Instances /** Queries activity instances, filtered by type, deserializing them as [AnyInstance]. **/ - fun instances(type: String) = instances(type, AnyInstance::deserialize) + fun instances(type: String) = instances(type, AnyInstance.deserializer()) /** Queries all activity instances, deserializing them as [AnyInstance]. **/ - fun instances() = instances(null, AnyInstance::deserialize) + fun instances() = instances(null, AnyInstance.deserializer()) } From 50e2f0dacf4097f6fb7f29b3bb10af338c2ced3f Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Thu, 5 Sep 2024 08:24:57 -0700 Subject: [PATCH 066/108] restore `scheduling_goal_analysis` table comment --- .../hasura/migrations/Aerie/10_procedural_scheduling/down.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql index 4956634b65..78afd45654 100644 --- a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql +++ b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql @@ -121,6 +121,9 @@ alter table scheduler.scheduling_goal_analysis drop column goal_invocation_id; +comment on table scheduler.scheduling_goal_analysis is e'' + 'The analysis of single goal from a scheduling run.'; + -- restore scheduling_specification_goals -- delete data for new PK -- i.e. find where (spec_id, goal_id) is not unique From 45bc2ab2995c9139ea2f2229097e772c099295a4 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Thu, 5 Sep 2024 08:26:44 -0700 Subject: [PATCH 067/108] fix sql comments about explictly setting not null --- .../migrations/Aerie/10_procedural_scheduling/down.sql | 2 +- .../migrations/Aerie/10_procedural_scheduling/up.sql | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql index 78afd45654..f8b7e4f945 100644 --- a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql +++ b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql @@ -40,7 +40,7 @@ alter table scheduler.scheduling_goal_analysis_satisfying_activities drop constraint satisfying_activities_primary_key, drop constraint satisfying_activities_references_scheduling_goal_analysis, - -- temp set as nullable so we can insert, made not null by PK constraint below + -- temp set as nullable so we can insert, made explictly not null below add column goal_id integer null, add column goal_revision integer null; diff --git a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql index 79b1b531b6..80f5525522 100644 --- a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql +++ b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql @@ -10,7 +10,8 @@ comment on column scheduler.scheduling_specification_goals.goal_invocation_id is -- update goal_analysis PK alter table scheduler.scheduling_goal_analysis - add column goal_invocation_id integer null, -- temp set as nullable so we can insert, made not null by PK constraint below + -- temp set as nullable so we can insert, made explictly not null below + add column goal_invocation_id integer null, drop constraint scheduling_goal_analysis_primary_key; @@ -39,7 +40,7 @@ alter table scheduler.scheduling_goal_analysis -- update created_activities PK alter table scheduler.scheduling_goal_analysis_created_activities drop constraint created_activities_primary_key, - -- temp set as nullable so we can insert, made not null by PK constraint below + -- temp set as nullable so we can insert, made explictly not null below add column goal_invocation_id integer null; comment on column scheduler.scheduling_goal_analysis_created_activities.goal_invocation_id is e'' @@ -72,7 +73,7 @@ alter table scheduler.scheduling_goal_analysis_created_activities -- update satisfing_activities PK alter table scheduler.scheduling_goal_analysis_satisfying_activities drop constraint satisfying_activities_primary_key, - -- temp set as nullable so we can insert, made not null by PK constraint below + -- temp set as nullable so we can insert, made explictly not null below add column goal_invocation_id integer null; comment on column scheduler.scheduling_goal_analysis_satisfying_activities.goal_invocation_id is e'' From 5912d589c9a8088b5a22be0b2e5fa14e2b9f2630 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Thu, 5 Sep 2024 08:31:00 -0700 Subject: [PATCH 068/108] inline `set not null` into existing PK table alter statements --- .../Aerie/10_procedural_scheduling/down.sql | 14 ++++++-------- .../Aerie/10_procedural_scheduling/up.sql | 19 ++++++++----------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql index f8b7e4f945..ef3586329c 100644 --- a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql +++ b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/down.sql @@ -71,16 +71,10 @@ set goal_id = ga.goal_id, goal_revision = ga.goal_revision from scheduler.scheduling_goal_analysis ga where (ca.analysis_id, ca.goal_invocation_id) = (ga.analysis_id, ga.goal_invocation_id); --- explictly restore non-nullability before PKing alter table scheduler.scheduling_goal_analysis_satisfying_activities + -- explictly restore non-nullability before PKing alter column goal_id set not null, - alter column goal_revision set not null; - -alter table scheduler.scheduling_goal_analysis_created_activities - alter column goal_id set not null, - alter column goal_revision set not null; - -alter table scheduler.scheduling_goal_analysis_satisfying_activities + alter column goal_revision set not null, add constraint satisfying_activities_primary_key primary key (analysis_id, goal_id, goal_revision, activity_id), @@ -93,6 +87,10 @@ alter table scheduler.scheduling_goal_analysis_satisfying_activities drop column goal_invocation_id; alter table scheduler.scheduling_goal_analysis_created_activities + -- explictly restore non-nullability before PKing + alter column goal_id set not null, + alter column goal_revision set not null, + add constraint created_activities_primary_key primary key (analysis_id, goal_id, goal_revision, activity_id), diff --git a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql index 80f5525522..7d48b297bd 100644 --- a/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql +++ b/deployment/hasura/migrations/Aerie/10_procedural_scheduling/up.sql @@ -29,11 +29,10 @@ from scheduler.scheduling_request as sr where sr.analysis_id = sga.analysis_id and sga.goal_id = ssg.goal_id; --- explictly set not null before PKing alter table scheduler.scheduling_goal_analysis - alter column goal_invocation_id set not null; + -- explictly set not null before PKing + alter column goal_invocation_id set not null, -alter table scheduler.scheduling_goal_analysis add constraint scheduling_goal_analysis_primary_key primary key (analysis_id, goal_invocation_id); @@ -54,14 +53,13 @@ from scheduler.scheduling_request as sr where sr.analysis_id = sgaca.analysis_id and sgaca.goal_id = ssg.goal_id; --- explictly set not null before PKing -alter table scheduler.scheduling_goal_analysis_created_activities - alter column goal_invocation_id set not null; - alter table scheduler.scheduling_goal_analysis_created_activities drop column goal_id, drop column goal_revision, + -- explictly set not null before PKing + alter column goal_invocation_id set not null, + add constraint created_activities_primary_key primary key (analysis_id, goal_invocation_id, activity_id), add constraint created_activities_references_scheduling_goal_analysis @@ -87,14 +85,13 @@ from scheduler.scheduling_request as sr where sr.analysis_id = sgasa.analysis_id and sgasa.goal_id = ssg.goal_id; --- explictly set not null before PKing -alter table scheduler.scheduling_goal_analysis_satisfying_activities - alter column goal_invocation_id set not null; - alter table scheduler.scheduling_goal_analysis_satisfying_activities drop column goal_id, drop column goal_revision, + -- explictly set not null before PKing + alter column goal_invocation_id set not null, + add constraint satisfying_activities_primary_key primary key (analysis_id, goal_invocation_id, activity_id), add constraint satisfying_activities_references_scheduling_goal_analysis From fdb6e81c88ce398a6bc82a03ddcddbf72e676052 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Thu, 5 Sep 2024 08:40:50 -0700 Subject: [PATCH 069/108] reset directiveId vars in `beforeEach` --- .../test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java index 5f1a81c0fc..b84d6254c7 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java @@ -47,6 +47,8 @@ public class SchedulingTests { private int modelId; private int planId; private int schedulingSpecId; + private int directiveId1; + private int directiveId2; // Cross-Test Constants private final String planStartTimestamp = "2023-01-01T00:00:00+00:00"; @@ -130,6 +132,9 @@ void beforeEach() throws IOException, InterruptedException { "24:00:00", planStartTimestamp); schedulingSpecId = hasura.getSchedulingSpecId(planId); + // Unset directiveId vars + directiveId1 = -1; + directiveId2 = -1; } @AfterEach @@ -139,8 +144,6 @@ void afterEach() throws IOException { hasura.deleteMissionModel(modelId); } - private int directiveId1 = -1; - private int directiveId2 = -1; private void insertActivities() throws IOException { // Duration argument is specified on one but not the other to verify that the scheduler can pick up on effective args From 05086a75c3ed1c3ff00055f078d448b0ea7de85e Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 5 Sep 2024 09:56:39 -0700 Subject: [PATCH 070/108] Minor edits --- .../examples/fooprocedures/procedures/SampleProcedure.java | 3 ++- .../examples/fooprocedures/procedures/SimulationDemo.java | 3 ++- .../nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt | 4 ++-- .../java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java | 1 - 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java index 5f56a43e6c..8e323ace0b 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java @@ -5,13 +5,14 @@ import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart; +import org.jetbrains.annotations.NotNull; import java.util.Map; @SchedulingProcedure public record SampleProcedure(int quantity) implements Goal { @Override - public void run(EditablePlan plan) { + public void run(@NotNull final EditablePlan plan) { final var firstTime = Duration.hours(24); final var step = Duration.hours(6); diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java index bbceb3bde0..5dd0f63af4 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java @@ -7,6 +7,7 @@ import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan; import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real; import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart; +import org.jetbrains.annotations.NotNull; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; @@ -17,7 +18,7 @@ @SchedulingProcedure public record SimulationDemo(int quantity) implements Goal { @Override - public void run(EditablePlan plan) { + public void run(@NotNull final EditablePlan plan) { // final var firstActivityTime = plan.toRelative(Instant.from(DOY_WITHOUT_ZONE_FORMATTER.parse("2024-128T07:00:00"))); // // plan.create( diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt index 24338fdba7..4212ea78bf 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt @@ -140,8 +140,8 @@ interface ParallelOps, THIS: ParallelOps>: GeneralOp /** [(DOC)][countActive] Returns a [Numbers] profile that corresponds to the number of active objects at any given time. */ fun countActive() = reduceIntoProfile(::Numbers, NullBinaryOperation.reduce( { _, _ -> 1 }, - { _, acc, _ -> acc.toInt() + 1} - )).assignGaps(Numbers(0)) + { _, acc, _ -> acc + 1} + )).assignGaps(0) /** * [(DOC)][accumulatedDuration] Creates a Real profile corresponding to the running total of time diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java index 18a91aada2..90c3ca9f9c 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java @@ -25,7 +25,6 @@ import static gov.nasa.jpl.aerie.scheduler.plan.InMemoryEditablePlan.toSchedulingActivityDirective; public class Procedure extends Goal { - // private final gov.nasa.jpl.aerie.scheduling.Procedure procedure; private final Path jarPath; private final SerializedValue args; From 7cb74b99765187e74d5e061660d067b8f869939e Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 5 Sep 2024 14:48:15 -0700 Subject: [PATCH 071/108] Fix plan duplication bug --- .../nasa/jpl/aerie/e2e/SchedulingTests.java | 32 ++----------------- .../fooprocedures/constraints/Procedure.java | 4 --- .../jpl/aerie/scheduler/goals/Procedure.java | 15 ++------- .../jpl/aerie/scheduler/model/Problem.java | 4 +-- .../CheckpointSimulationFacade.java | 19 ++--------- .../scheduler/plan/InMemoryEditablePlan.kt | 6 ++-- .../plan/SchedulerToProcedurePlanAdapter.kt | 13 ++++---- 7 files changed, 17 insertions(+), 76 deletions(-) delete mode 100644 procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Procedure.java diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java index b84d6254c7..458b44f714 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java @@ -47,8 +47,6 @@ public class SchedulingTests { private int modelId; private int planId; private int schedulingSpecId; - private int directiveId1; - private int directiveId2; // Cross-Test Constants private final String planStartTimestamp = "2023-01-01T00:00:00+00:00"; @@ -133,8 +131,6 @@ void beforeEach() throws IOException, InterruptedException { planStartTimestamp); schedulingSpecId = hasura.getSchedulingSpecId(planId); // Unset directiveId vars - directiveId1 = -1; - directiveId2 = -1; } @AfterEach @@ -147,8 +143,8 @@ void afterEach() throws IOException { private void insertActivities() throws IOException { // Duration argument is specified on one but not the other to verify that the scheduler can pick up on effective args - directiveId1 = hasura.insertActivityDirective(planId, "GrowBanana", "1h", JsonValue.EMPTY_JSON_OBJECT); - directiveId2 = hasura.insertActivityDirective(planId, "GrowBanana", "3h", Json.createObjectBuilder() + hasura.insertActivityDirective(planId, "GrowBanana", "1h", JsonValue.EMPTY_JSON_OBJECT); + hasura.insertActivityDirective(planId, "GrowBanana", "3h", Json.createObjectBuilder() .add("growingDuration", 7200000000L) // 2h .build()); hasura.updatePlanRevisionSchedulingSpec(planId); @@ -607,30 +603,6 @@ void injectedResultsLoaded() throws IOException{ plantType, List.of(new ProfileSegment("0h", false, Json.createValue(400)))); - hasura.insertActivityInstance( - datasetId, - directiveId1, - "GrowBanana", - "1h", - "1h", - Json.createObjectBuilder() - .add("quantity", 1) - .add("growingDuration", 3600000000L) - .build() - ); - - hasura.insertActivityInstance( - datasetId, - directiveId2, - "GrowBanana", - "3h", - "2h", - Json.createObjectBuilder() - .add("quantity", 1) - .add("growingDuration", 7200000000L) - .build() - ); - // Insert Goal final int plantGoal = hasura.createSchedulingSpecGoal( "Scheduling Test: When Plant < 300", diff --git a/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Procedure.java b/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Procedure.java deleted file mode 100644 index cd1cd5927d..0000000000 --- a/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Procedure.java +++ /dev/null @@ -1,4 +0,0 @@ -package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.constraints; - -public interface Procedure { -} diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java index 90c3ca9f9c..a4ed23538d 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java @@ -15,14 +15,13 @@ import gov.nasa.jpl.aerie.scheduler.simulation.SimulationFacade; import gov.nasa.jpl.aerie.scheduler.solver.ConflictSatisfaction; import gov.nasa.jpl.aerie.scheduler.solver.Evaluation; -import org.apache.commons.lang3.NotImplementedException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.function.Function; -import static gov.nasa.jpl.aerie.scheduler.plan.InMemoryEditablePlan.toSchedulingActivityDirective; +import static gov.nasa.jpl.aerie.scheduler.plan.InMemoryEditablePlan.toSchedulingActivity; public class Procedure extends Goal { private final Path jarPath; @@ -58,16 +57,6 @@ public void run(Evaluation eval, Plan plan, MissionModel missionModel, Functi lookupActivityType::apply ); - /* - TODO - - Comments from Joel: - - Part of the intent of editablePlan was to be able to re-use it across procedures. - - Could be done by initializing EditablePlanImpl with simulation results - - Duration construction and arithmetic can be less awkward - */ - procedureMapper.deserialize(this.args).run(editablePlan); if (!editablePlan.getUncommittedChanges().isEmpty()) { @@ -75,7 +64,7 @@ public void run(Evaluation eval, Plan plan, MissionModel missionModel, Functi } for (final var edit : editablePlan.getTotalDiff()) { if (edit instanceof Edit.Create c) { - newActivities.add(toSchedulingActivityDirective(c.getDirective(), lookupActivityType::apply, true)); + newActivities.add(toSchedulingActivity(c.getDirective(), lookupActivityType::apply, true)); } else { throw new IllegalStateException("Unexpected value: " + edit); } diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/Problem.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/Problem.java index 1af4804706..a2ffc9e7f7 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/Problem.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/Problem.java @@ -134,7 +134,7 @@ public MissionModel getMissionModel() { * @return the initial seed plan that schedulers may start from */ public Plan getInitialPlan() { - return initialPlan; + return initialPlan.duplicate(); } /** @@ -148,7 +148,7 @@ public void setInitialPlan( ) { initialPlan = plan; this.initialSimulationResults = initialSimulationResults.map(simulationResults -> new SimulationData( - plan, + getInitialPlan(), simulationResults, SimulationResultsConverter.convertToConstraintModelResults(simulationResults) )); diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java index f5fb07efad..834bac8896 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacade.java @@ -312,23 +312,8 @@ public SimulationData simulateWithResults( ) throws SimulationException, SchedulingInterruptedException { if (this.initialSimulationResults != null) { final var inputPlan = scheduleFromPlan(plan, schedulerModel); - final HashMap planActuallySimulated = new HashMap<>(); - final var initialPlan = this.initialSimulationResults.plan().getActivitiesById(); - var allActivitiesFound = true; - for (final var activityInstance: this.initialSimulationResults.constraintsResults().activities) { - if (activityInstance.directiveId().isPresent()) { - final var directiveId = activityInstance.directiveId().get(); - final var directive = initialPlan.get(directiveId); - if (directive != null) { - planActuallySimulated.put(activityInstance.directiveId().get(), schedulingActToActivityDir(directive, schedulerModel)); - } else { - allActivitiesFound = false; - } - } - } - if (allActivitiesFound && inputPlan.equals(new PlanSimCorrespondence(planActuallySimulated))) { - return initialSimulationResults; - } + final var initialPlan = scheduleFromPlan(this.initialSimulationResults.plan(), schedulerModel); + if (inputPlan.equals(initialPlan)) return initialSimulationResults; } final var resultsInput = simulateNoResults(plan, until); final var driverResults = resultsInput.simulationResultsComputerInputs().computeResults(resourceNames); diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt index 26591b71c6..2652c03abf 100644 --- a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/InMemoryEditablePlan.kt @@ -54,7 +54,7 @@ data class InMemoryEditablePlan( val resolved = directive.resolve(id, parent) uncommittedChanges.add(Edit.Create(resolved)) resolved.validateArguments(lookupActivityType) - plan.add(resolved.toSchedulingActivityDirective(lookupActivityType, true)) + plan.add(resolved.toSchedulingActivity(lookupActivityType, true)) return id } @@ -70,7 +70,7 @@ data class InMemoryEditablePlan( for (edit in result) { when (edit) { is Edit.Create -> { - plan.remove(edit.directive.toSchedulingActivityDirective(lookupActivityType, true)) + plan.remove(edit.directive.toSchedulingActivity(lookupActivityType, true)) } } } @@ -93,7 +93,7 @@ data class InMemoryEditablePlan( lookupActivityType(type).specType.inputType.validateArguments(inner.arguments) } - @JvmStatic fun Directive.toSchedulingActivityDirective(lookupActivityType: (String) -> ActivityType, isNew: Boolean) = SchedulingActivity( + @JvmStatic fun Directive.toSchedulingActivity(lookupActivityType: (String) -> ActivityType, isNew: Boolean) = SchedulingActivity( id, lookupActivityType(type), when (val s = start) { diff --git a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt index b7c7b46674..71a1b78263 100644 --- a/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt +++ b/scheduler-driver/src/main/kotlin/gov/nasa/jpl/aerie/scheduler/plan/SchedulerToProcedurePlanAdapter.kt @@ -1,19 +1,18 @@ package gov.nasa.jpl.aerie.scheduler.plan -import gov.nasa.jpl.aerie.merlin.protocol.types.Duration -import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue -import gov.nasa.jpl.aerie.scheduler.model.ActivityType -import gov.nasa.jpl.aerie.scheduler.model.PlanningHorizon import gov.nasa.ammos.aerie.procedural.timeline.Interval import gov.nasa.ammos.aerie.procedural.timeline.collections.Directives -import gov.nasa.ammos.aerie.procedural.timeline.util.duration.minus -import gov.nasa.ammos.aerie.procedural.timeline.util.duration.plus import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.Directive import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart.Anchor.AnchorPoint.Companion.anchorToStart +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.minus +import gov.nasa.ammos.aerie.procedural.timeline.util.duration.plus +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue +import gov.nasa.jpl.aerie.scheduler.model.PlanningHorizon import java.time.Instant -import gov.nasa.jpl.aerie.scheduler.model.Plan as SchedulerPlan import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan as TimelinePlan +import gov.nasa.jpl.aerie.scheduler.model.Plan as SchedulerPlan data class SchedulerToProcedurePlanAdapter( private val schedulerPlan: SchedulerPlan, From 413f17d760d92ff6ad33071b139c39cea57713f9 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Thu, 5 Sep 2024 14:41:34 -0700 Subject: [PATCH 072/108] use `GoalSource` instead of string for additional context --- .../gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java | 2 +- .../server/remotes/postgres/GetSchedulingGoalAction.java | 4 +--- .../server/remotes/postgres/GetSpecificationGoalsAction.java | 2 +- .../scheduler/worker/services/SynchronousSchedulerAgent.java | 5 +++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java index 7ca3cc6fec..0aa89e456a 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java @@ -4,6 +4,6 @@ import java.nio.file.Path; public sealed interface GoalType { - record EDSL(String source) implements GoalType {} + record EDSL(GoalSource source) implements GoalType {} record JAR(Path path, String args) implements GoalType {} } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java index 0a32afd1a3..4e40e1a9f4 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java @@ -10,8 +10,6 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; import java.util.Optional; /*package-local*/ final class GetSchedulingGoalAction implements AutoCloseable { @@ -44,7 +42,7 @@ public Optional get(final GoalId goalId) throws SQLException { return Optional.of(new GoalRecord( goalId, name, - type.equals("JAR") ? new GoalType.JAR(Path.of(path), "" /* TODO this is a property of the specification, not the goal */) : new GoalType.EDSL(definition), + type.equals("JAR") ? new GoalType.JAR(Path.of(path), "" /* TODO this is a property of the specification, not the goal */) : new GoalType.EDSL(new GoalSource(definition)), true // TODO this is not a property of the goal, but rather of the specification )); } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java index 00e61a6cf7..ec6f14f762 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java @@ -55,7 +55,7 @@ public List get(final long specificationId) throws SQLException { goals.add(new GoalRecord( new GoalId(id, revision, Optional.of(goalInvocationId)), name, - type.equals("JAR") ? new GoalType.JAR(Path.of(path), args) : new GoalType.EDSL(definition), + type.equals("JAR") ? new GoalType.JAR(Path.of(path), args) : new GoalType.EDSL(new GoalSource(definition)), simulateAfter )); } diff --git a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java index d8b793d0eb..7d159ad975 100644 --- a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java +++ b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java @@ -54,6 +54,7 @@ import gov.nasa.jpl.aerie.scheduler.server.models.ExternalProfiles; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalSource; import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import gov.nasa.jpl.aerie.scheduler.server.models.MerlinPlan; import gov.nasa.jpl.aerie.scheduler.server.models.PlanId; @@ -361,14 +362,14 @@ private Optional storeSimulationResults( private static SchedulingDSLCompilationService.SchedulingDSLCompilationResult compileGoalDefinition( final MerlinDatabaseService.ReaderRole merlinDatabaseService, final PlanId planId, - final String source, + final GoalSource source, final SchedulingDSLCompilationService schedulingDSLCompilationService, final Collection additionalResourceTypes) { return schedulingDSLCompilationService.compileSchedulingGoalDSL( merlinDatabaseService, planId, - source, + source.source(), additionalResourceTypes ); } From ecb8f5f8f733fdd2ea3523011b5f95875ebf58a4 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Thu, 5 Sep 2024 14:58:53 -0700 Subject: [PATCH 073/108] update to `com.gradleup.shadow` --- procedural/constraints/build.gradle | 2 +- procedural/remote/build.gradle | 2 +- procedural/scheduling/build.gradle | 2 +- procedural/timeline/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/procedural/constraints/build.gradle b/procedural/constraints/build.gradle index 5d149d08b2..2749cc1aeb 100644 --- a/procedural/constraints/build.gradle +++ b/procedural/constraints/build.gradle @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { - id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'com.gradleup.shadow' version '8.3.0' id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' diff --git a/procedural/remote/build.gradle b/procedural/remote/build.gradle index 82b13ab83f..00bade518b 100644 --- a/procedural/remote/build.gradle +++ b/procedural/remote/build.gradle @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { - id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'com.gradleup.shadow' version '8.3.0' id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' diff --git a/procedural/scheduling/build.gradle b/procedural/scheduling/build.gradle index 4377e02fe8..a52d345db8 100644 --- a/procedural/scheduling/build.gradle +++ b/procedural/scheduling/build.gradle @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { - id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'com.gradleup.shadow' version '8.3.0' id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' diff --git a/procedural/timeline/build.gradle b/procedural/timeline/build.gradle index 743f1479b8..6e81b03dd5 100644 --- a/procedural/timeline/build.gradle +++ b/procedural/timeline/build.gradle @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { - id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'com.gradleup.shadow' version '8.3.0' id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' From f8f3527629d13acfe645921aa9aaa382dab5c3d5 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Thu, 5 Sep 2024 15:20:09 -0700 Subject: [PATCH 074/108] use `GoalSource` in test instantiation --- .../worker/services/SchedulingEdslIntegrationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java index eb5dd506b2..29f6c386fb 100644 --- a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java +++ b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java @@ -2198,7 +2198,7 @@ private SchedulingRunResults runScheduler( final var goalsByPriority = new ArrayList(); for (final var goal : goals) { - goalsByPriority.add(new GoalRecord(goal.goalId(), "test goal", new GoalType.EDSL(goal.definition()), goal.simulateAfter())); + goalsByPriority.add(new GoalRecord(goal.goalId(), "test goal", new GoalType.EDSL(new GoalSource(goal.definition())), goal.simulateAfter())); } final var specificationService = new SpecificationService(new MockSpecificationRepository(Map.of(new SpecificationId(1L), new Specification( new SpecificationId(1L), From 5d1ca870b2dde8f78bdb6377e593c8e0cb8d26d6 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Thu, 5 Sep 2024 16:05:00 -0700 Subject: [PATCH 075/108] also find and replace goooler plugin --- procedural/examples/foo-procedures/build.gradle | 6 +----- procedural/processor/build.gradle | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/procedural/examples/foo-procedures/build.gradle b/procedural/examples/foo-procedures/build.gradle index 3ba155b189..721e290833 100644 --- a/procedural/examples/foo-procedures/build.gradle +++ b/procedural/examples/foo-procedures/build.gradle @@ -2,7 +2,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { id 'java' - id 'io.github.goooler.shadow' version '8.1.7' + id 'com.gradleup.shadow' version '8.3.0' } java { @@ -69,10 +69,6 @@ tasks.create("generateSchedulingProcedureJarTasks") { attributes 'Main-Class': getMainClassFromGeneratedFile(file) } minimize() - dependencies { - // exclude project(':procedural:timeline') - // exclude dependency(":kotlin.*") - } } } } diff --git a/procedural/processor/build.gradle b/procedural/processor/build.gradle index 405af8f39f..982f7d732f 100644 --- a/procedural/processor/build.gradle +++ b/procedural/processor/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java-library' id 'maven-publish' - id 'io.github.goooler.shadow' version '8.1.7' + id 'com.gradleup.shadow' version '8.3.0' } java { From da3b564a7d8cc4f8bd171f590c50347f874e1212 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 5 Sep 2024 16:09:08 -0700 Subject: [PATCH 076/108] Fix foo-procedures readme --- procedural/examples/foo-procedures/README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/procedural/examples/foo-procedures/README.md b/procedural/examples/foo-procedures/README.md index cfbac1442a..e24db049f1 100644 --- a/procedural/examples/foo-procedures/README.md +++ b/procedural/examples/foo-procedures/README.md @@ -1,11 +1,13 @@ # foo-procedures -This is an example project for using procedural-based constraints and scheduling procedures. +This is a project for test examples. It does provide valid examples +of goals and constraints, but they aren't particularly helpful for users. -The main utility this subproject provides is the following gradle tasks, which provide automation for compiling and packaging large numbers of procedure / constraint jars, which will be uploaded to the Aerie system down the line. +## Building -## `generateConstraintJarTasks` +At repo top level: -Iterates over all source files in - -## `buildAllConstraintJars` +```bash +./gradlew build +./gradlew :procedural:examples:foo-procedures:buildAllSchedulingProcedureJars +``` From 249da9c1d7932b68667ae0748f7f2c0c52c5c5ac Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 5 Sep 2024 16:13:59 -0700 Subject: [PATCH 077/108] Clarify remote docs --- procedural/remote/MODULE_DOCS.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/procedural/remote/MODULE_DOCS.md b/procedural/remote/MODULE_DOCS.md index fe3e3e0f53..4e259d6c51 100644 --- a/procedural/remote/MODULE_DOCS.md +++ b/procedural/remote/MODULE_DOCS.md @@ -1,8 +1,9 @@ # Module Remote This library provides tools for accessing plans and simulation results from remote execution environments. -This is intended to allow goal and constraint authors to run their code outside Aerie, and these implementations -do not need to be packaged in the goal and constraint jars. +This is intended to allow goal and constraint authors to run their code locally while remotely interacting +with an Aerie instance. These classes do not need to be packaged inside the jars, and instead should be +imported by a driver that the user implements themselves. # Package gov.nasa.ammos.aerie.procedural.remote -Remote utilities for running goals and constraints outside of Aerie. +Utilities for using goals and constraints to locally interact with a remote Aerie instance. From 3724f8387cd0e717d38018242d68d8d0471e0483 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 5 Sep 2024 16:18:46 -0700 Subject: [PATCH 078/108] Implement GoalInvocationId.fromJSON --- .../nasa/jpl/aerie/e2e/types/GoalInvocationId.java | 11 ++++++++++- .../gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalInvocationId.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalInvocationId.java index 07acdc4cf6..9694940c7a 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalInvocationId.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/GoalInvocationId.java @@ -1,3 +1,12 @@ package gov.nasa.jpl.aerie.e2e.types; -public record GoalInvocationId(int goalId, int invocationId) { } +import javax.json.JsonObject; + +public record GoalInvocationId(int goalId, int invocationId) { + public static GoalInvocationId fromJSON(JsonObject json) { + return new GoalInvocationId( + json.getInt("goal_id"), + json.getInt("goal_invocation_id") + ); + } +} diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java index 71a81e6ea6..385dc3327e 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java @@ -700,7 +700,7 @@ public GoalInvocationId createSchedulingSpecProcedure( final var resp = makeRequest(GQL.CREATE_SCHEDULING_SPEC_GOAL, variables) .getJsonObject("insert_scheduling_specification_goals_one"); - return new GoalInvocationId(resp.getInt("goal_id"), resp.getInt("goal_invocation_id")); + return GoalInvocationId.fromJSON(resp); } public GoalInvocationId createSchedulingSpecGoal( @@ -738,7 +738,7 @@ public GoalInvocationId createSchedulingSpecGoal( final var resp = makeRequest(GQL.CREATE_SCHEDULING_SPEC_GOAL, variables) .getJsonObject("insert_scheduling_specification_goals_one"); - return new GoalInvocationId(resp.getInt("goal_id"), resp.getInt("goal_invocation_id")); + return GoalInvocationId.fromJSON(resp); } public int updateGoalDefinition(int goalId, String definition) throws IOException { From c57a14948b97a419e17e46a56429fb58cad74eed Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 5 Sep 2024 16:24:39 -0700 Subject: [PATCH 079/108] Clarify duration range explanation --- .../gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java b/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java index fea63c43dd..06d30a0689 100644 --- a/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java +++ b/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java @@ -38,9 +38,9 @@ * Merlin-provided units like {@code SECONDS}.

* *

A time value is represented as a {@code long} where an increment maps to number a specific time unit. Currently, - * the underlying time unit is microseconds, however, one should not rely on this always being the case. The maximum - * value of a fixed-point type is the half of the largest value that can be represented by the underlying integer type. - * For a {@code long} this yields a range of -2^62 to 2^62, or about 146,000 years in the future and past, at microsecond + * the underlying time unit is microseconds, however, one should not rely on this always being the case. The Duration type + * restricts this {@code long} to a range from half of long-min to half of long-max. + * This yields a range of -2^62 to 2^62, or about 146,000 years in the future and past, at microsecond * resolution.

*

From 2e35b1416b64cb72e888e43dd1f3937a9ce05530 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Thu, 5 Sep 2024 16:29:25 -0700 Subject: [PATCH 080/108] Remove obsolete scheduling goal jar env var --- deployment/Environment.md | 1 - 1 file changed, 1 deletion(-) diff --git a/deployment/Environment.md b/deployment/Environment.md index d8556be552..2c64972d76 100644 --- a/deployment/Environment.md +++ b/deployment/Environment.md @@ -68,7 +68,6 @@ See the [environment variables document](https://github.com/NASA-AMMOS/aerie-gat | `SCHEDULER_DB_USER` | Username of the Scheduler DB User | `string` | scheduler_service | | `SCHEDULER_DB_PASSWORD` | Password of the Scheduler DB User | `string` | | | `SCHEDULER_OUTPUT_MODE` | How scheduler output is sent back to Aerie | `string` | UpdateInputPlanWithNewActivities | -| `SCHEDULER_RULES_JAR` | Jar file to load scheduling rules from (until user input to database) | `string` | /usr/src/app/merlin_file_store/scheduler_rules.jar | | `MAX_NB_CACHED_SIMULATION_ENGINES` | The maximum number of simulation engines to cache in memory during a scheduling run. Must be at least 1 | `number` | 1 | ## Aerie Sequencing From 6f88a2fb3476d98936e5362aa219d3ac2f8b0d15 Mon Sep 17 00:00:00 2001 From: luke Date: Thu, 5 Sep 2024 16:41:04 -0700 Subject: [PATCH 081/108] Update scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java Co-authored-by: Mythicaeda --- .../server/remotes/postgres/GetCreatedActivitiesAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java index 10bbe8ae46..642cc661d7 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java @@ -21,8 +21,8 @@ c.goal_invocation_id, c.activity_id from scheduler.scheduling_goal_analysis_created_activities as c - join scheduler.scheduling_goal_analysis as a using (goal_invocation_id) - where c.analysis_id = ? and a.analysis_id = ? + join scheduler.scheduling_goal_analysis as a using (analysis_id, goal_invocation_id) + where c.analysis_id = ?; """; private final PreparedStatement statement; From 935328f7d83c8f07c82d465500796c854ae1f5e0 Mon Sep 17 00:00:00 2001 From: luke Date: Thu, 5 Sep 2024 17:00:27 -0700 Subject: [PATCH 082/108] Update scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java Co-authored-by: Mythicaeda --- .../postgres/UpdateSchedulingGoalParameterSchemaAction.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java index b5d3bcfdb6..579291ad5d 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java @@ -15,7 +15,9 @@ /*package-local*/ final class UpdateSchedulingGoalParameterSchemaAction implements AutoCloseable { private final @Language("SQL") String sql = """ - update scheduler.scheduling_goal_definition gd set parameter_schema=?::jsonb where gd.goal_id = ? and gd.revision = ?; + update scheduler.scheduling_goal_definition gd + set parameter_schema=?::jsonb + where gd.goal_id = ? and gd.revision = ?; """; private final PreparedStatement statement; From 556e4e847721e1a6694aace1232c335e4d6193f8 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Thu, 5 Sep 2024 17:12:38 -0700 Subject: [PATCH 083/108] Remove references to SCHEDULER_RULES_JAR --- deployment/docker-compose.yml | 1 - deployment/kubernetes/aerie-scheduler-worker-deployment.yaml | 2 -- docker-compose.yml | 2 -- e2e-tests/docker-compose-many-workers.yml | 1 - e2e-tests/docker-compose-test.yml | 2 -- 5 files changed, 8 deletions(-) diff --git a/deployment/docker-compose.yml b/deployment/docker-compose.yml index deb4cd0d7f..e199edf9ca 100755 --- a/deployment/docker-compose.yml +++ b/deployment/docker-compose.yml @@ -99,7 +99,6 @@ services: SCHEDULER_DB_PASSWORD: "${SCHEDULER_PASSWORD}" SCHEDULER_OUTPUT_MODE: UpdateInputPlanWithNewActivities MERLIN_LOCAL_STORE: /usr/src/app/merlin_file_store - SCHEDULER_RULES_JAR: /usr/src/app/merlin_file_store/scheduler_rules.jar MAX_NB_CACHED_SIMULATION_ENGINES: 1 JAVA_OPTS: > -Dorg.slf4j.simpleLogger.log.com.zaxxer.hikari=INFO diff --git a/deployment/kubernetes/aerie-scheduler-worker-deployment.yaml b/deployment/kubernetes/aerie-scheduler-worker-deployment.yaml index ffac8bb87b..41279bbee1 100644 --- a/deployment/kubernetes/aerie-scheduler-worker-deployment.yaml +++ b/deployment/kubernetes/aerie-scheduler-worker-deployment.yaml @@ -38,8 +38,6 @@ spec: value: /usr/src/app/merlin_file_store - name: SCHEDULER_OUTPUT_MODE value: UpdateInputPlanWithNewActivities - - name: SCHEDULER_RULES_JAR - value: /usr/src/app/merlin_file_store/scheduler_rules.jar - name: AERIE_DB_PORT value: "5432" - name: AERIE_DB_HOST diff --git a/docker-compose.yml b/docker-compose.yml index 7d1bb72f30..4a03eb58a9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -178,7 +178,6 @@ services: SCHEDULER_DB_PASSWORD: "${SCHEDULER_PASSWORD}" SCHEDULER_OUTPUT_MODE: UpdateInputPlanWithNewActivities MERLIN_LOCAL_STORE: /usr/src/app/merlin_file_store - SCHEDULER_RULES_JAR: /usr/src/app/merlin_file_store/scheduler_rules.jar MAX_NB_CACHED_SIMULATION_ENGINES: 1 JAVA_OPTS: > -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 @@ -205,7 +204,6 @@ services: SCHEDULER_DB_PASSWORD: "${SCHEDULER_PASSWORD}" SCHEDULER_OUTPUT_MODE: UpdateInputPlanWithNewActivities MERLIN_LOCAL_STORE: /usr/src/app/merlin_file_store - SCHEDULER_RULES_JAR: /usr/src/app/merlin_file_store/scheduler_rules.jar MAX_NB_CACHED_SIMULATION_ENGINES: 1 JAVA_OPTS: > -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 diff --git a/e2e-tests/docker-compose-many-workers.yml b/e2e-tests/docker-compose-many-workers.yml index bade896cae..57ff112182 100644 --- a/e2e-tests/docker-compose-many-workers.yml +++ b/e2e-tests/docker-compose-many-workers.yml @@ -152,7 +152,6 @@ services: SCHEDULER_DB_PASSWORD: "${SCHEDULER_PASSWORD}" SCHEDULER_OUTPUT_MODE: UpdateInputPlanWithNewActivities MERLIN_LOCAL_STORE: /usr/src/app/merlin_file_store - SCHEDULER_RULES_JAR: /usr/src/app/merlin_file_store/scheduler_rules.jar JAVA_OPTS: > -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG diff --git a/e2e-tests/docker-compose-test.yml b/e2e-tests/docker-compose-test.yml index 0bd560e423..c36d1b9c34 100644 --- a/e2e-tests/docker-compose-test.yml +++ b/e2e-tests/docker-compose-test.yml @@ -173,7 +173,6 @@ services: SCHEDULER_DB_PASSWORD: "${SCHEDULER_PASSWORD}" SCHEDULER_OUTPUT_MODE: UpdateInputPlanWithNewActivities MERLIN_LOCAL_STORE: /usr/src/app/merlin_file_store - SCHEDULER_RULES_JAR: /usr/src/app/merlin_file_store/scheduler_rules.jar JAVA_OPTS: > -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG @@ -198,7 +197,6 @@ services: SCHEDULER_DB_PASSWORD: "${SCHEDULER_PASSWORD}" SCHEDULER_OUTPUT_MODE: UpdateInputPlanWithNewActivities MERLIN_LOCAL_STORE: /usr/src/app/merlin_file_store - SCHEDULER_RULES_JAR: /usr/src/app/merlin_file_store/scheduler_rules.jar JAVA_OPTS: > -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG From 15d93de5d531ec8f6867dd06d731c7772cb82246 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 5 Sep 2024 17:23:11 -0700 Subject: [PATCH 084/108] Add ParallelOps.highlightAll() --- .../nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt index 4212ea78bf..2d7650e6a5 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/ops/ParallelOps.kt @@ -3,6 +3,7 @@ package gov.nasa.ammos.aerie.procedural.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration import gov.nasa.ammos.aerie.procedural.timeline.* import gov.nasa.ammos.aerie.procedural.timeline.collections.Universal +import gov.nasa.ammos.aerie.procedural.timeline.collections.Windows import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Booleans import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceNoOp @@ -22,6 +23,9 @@ interface ParallelOps, THIS: ParallelOps>: GeneralOp override fun isAlwaysSorted() = false + /** [(DOC)][highlightAll] Highlights all objects in the timeline in a new [Windows] timeline. */ + fun highlightAll() = unsafeMap(::Windows, BoundsTransformer.IDENTITY, true) { it.interval } + /** [(DOC)][merge] Combines two timelines together by overlaying them. Does not perform any transformation. */ infix fun merge(other: GeneralOps) = unsafeOperate { opts -> collect(opts) + other.collect(opts) From 86fc313026f630d0afc0f581153e849fbcc6ef45 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 5 Sep 2024 17:23:28 -0700 Subject: [PATCH 085/108] Delete Duration.isEqualTo --- .../gov/nasa/jpl/aerie/constraints/time/Interval.java | 10 ++++------ .../jpl/aerie/constraints/time/IntervalAlgebra.java | 10 +++++----- .../nasa/jpl/aerie/constraints/time/IntervalMap.java | 9 ++++----- .../jpl/aerie/contrib/streamline/core/Resources.java | 2 +- .../streamline/modeling/polynomial/Polynomial.java | 5 +---- .../foomissionmodel/FooSimulationDuplicationTest.java | 4 ++-- .../merlin/driver/CheckpointSimulationDriver.java | 4 ++-- .../jpl/aerie/merlin/driver/SimulationResults.java | 2 +- .../gov/nasa/jpl/aerie/merlin/framework/Condition.java | 4 ++-- .../nasa/jpl/aerie/merlin/protocol/types/Duration.java | 5 ----- .../simulation/SimulationExtentConsumer.java | 2 +- .../jpl/aerie/scheduler/EquationSolvingAlgorithms.java | 4 ++-- .../jpl/aerie/scheduler/model/SchedulingActivity.java | 4 ++-- .../simulation/InMemoryCachedEngineStore.java | 2 +- .../jpl/aerie/scheduler/solver/PrioritySolver.java | 4 ++-- .../gov/nasa/jpl/aerie/scheduler/RootfindingTest.java | 2 +- .../simulation/CheckpointSimulationFacadeTest.java | 2 +- .../services/SchedulingEdslIntegrationTests.java | 8 ++++---- 18 files changed, 36 insertions(+), 47 deletions(-) diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Interval.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Interval.java index b3197c20f2..a329451ade 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Interval.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Interval.java @@ -1,9 +1,7 @@ package gov.nasa.jpl.aerie.constraints.time; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; -import org.apache.commons.lang3.tuple.Pair; -import java.util.Comparator; import java.util.Objects; import static gov.nasa.jpl.aerie.constraints.time.Interval.Inclusivity.Exclusive; @@ -265,7 +263,7 @@ public boolean contains(Interval x){ } public boolean isSingleton(){ - return this.start.isEqualTo(this.end); + return this.start.equals(this.end); } public static Interval betweenClosedOpen(final Duration start, final Duration end) { @@ -286,7 +284,7 @@ public int compareTo(final Interval o) { public static int compareStartToStart(final Interval x, final Interval y) { // First, order by absolute time. - if (!x.start.isEqualTo(y.start)) { + if (!x.start.equals(y.start)) { return x.start.compareTo(y.start); } @@ -300,7 +298,7 @@ public static int compareStartToStart(final Interval x, final Interval y) { public static int compareEndToEnd(final Interval x, final Interval y) { // First, order by absolute time. - if (!x.end.isEqualTo(y.end)) { + if (!x.end.equals(y.end)) { return x.end.compareTo(y.end); } @@ -348,7 +346,7 @@ public static int compareEndToStart(final Interval x, final Interval y) { } public static boolean meets(final Interval x, final Interval y) { - return (x.end.isEqualTo(y.start)) && (x.endInclusivity != y.startInclusivity); + return (x.end.equals(y.start)) && (x.endInclusivity != y.startInclusivity); } public static boolean metBy(final Interval x, final Interval y) { diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/IntervalAlgebra.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/IntervalAlgebra.java index ca41c5d1ae..b7a6fc4ed6 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/IntervalAlgebra.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/IntervalAlgebra.java @@ -111,7 +111,7 @@ public static Interval strictUpperBoundsOf(final Interval x) { */ public static boolean startBeforeStart(Interval x, Interval y) { return x.start.shorterThan(y.start) || - (x.start.isEqualTo(y.start) && (x.includesStart() && !y.includesStart())); + (x.start.equals(y.start) && (x.includesStart() && !y.includesStart())); } /** @@ -123,7 +123,7 @@ public static boolean startBeforeStart(Interval x, Interval y) { */ public static boolean endBeforeStart(Interval x, Interval y) { return x.end.shorterThan(y.start) || - (x.end.isEqualTo(y.start) && (!x.includesEnd() || !y.includesStart())); + (x.end.equals(y.start) && (!x.includesEnd() || !y.includesStart())); } /** @@ -135,7 +135,7 @@ public static boolean endBeforeStart(Interval x, Interval y) { */ public static boolean endBeforeEnd(Interval x, Interval y) { return x.end.shorterThan(y.end) || - (x.end.isEqualTo(y.end) && (!x.includesEnd() && y.includesEnd())); + (x.end.equals(y.end) && (!x.includesEnd() && y.includesEnd())); } /** @@ -265,7 +265,7 @@ static boolean startsStrictlyAfter(Interval x, Interval y) { static boolean endsStrictlyBefore(Interval x, Interval y) { if (x.isEmpty() || y.isEmpty()) return false; return x.end.shorterThan(y.start) || - (x.end.isEqualTo(y.start) && (!x.includesEnd() && !y.includesStart())); + (x.end.equals(y.start) && (!x.includesEnd() && !y.includesStart())); } /** @@ -277,7 +277,7 @@ static boolean endsStrictlyBefore(Interval x, Interval y) { */ static boolean meets(Interval x, Interval y) { if (x.isEmpty() || y.isEmpty()) return false; - return x.end.isEqualTo(y.start) && (x.endInclusivity != y.startInclusivity); + return x.end.equals(y.start) && (x.endInclusivity != y.startInclusivity); } /** diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/IntervalMap.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/IntervalMap.java index 2450304594..06e254931c 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/IntervalMap.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/IntervalMap.java @@ -3,7 +3,6 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; import org.apache.commons.lang3.function.TriFunction; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; @@ -84,7 +83,7 @@ private static boolean invariantsMet(Iterable> segments) { if (segment.interval().isEmpty() || (oldSegment != null && (!endBeforeStart(oldSegment.interval(), segment.interval()) || - (segment.interval().start.isEqualTo(oldSegment.interval().end) && Objects.equals(segment.value(), oldSegment.value()))))) { + (segment.interval().start.equals(oldSegment.interval().end) && Objects.equals(segment.value(), oldSegment.value()))))) { segmentsOkay = false; break; } @@ -324,7 +323,7 @@ IntervalMap map2( if (!leftDone && (!leftGetNext || leftIter.hasNext())) { if (leftGetNext) leftNextDefinedSegment = leftIter.next(); leftGetNext = false; - if (leftNextDefinedSegment.interval().start.shorterThan(startTime) || (leftNextDefinedSegment.interval().start.isEqualTo(startTime) && !leftNextDefinedSegment.interval().startInclusivity.moreRestrictiveThan(startInclusivity))) { + if (leftNextDefinedSegment.interval().start.shorterThan(startTime) || (leftNextDefinedSegment.interval().start.equals(startTime) && !leftNextDefinedSegment.interval().startInclusivity.moreRestrictiveThan(startInclusivity))) { leftInterval = leftNextDefinedSegment.interval(); leftValue = Optional.of(leftNextDefinedSegment.value()); leftGetNext = true; @@ -345,7 +344,7 @@ IntervalMap map2( if (!rightDone && (!rightGetNext || rightIter.hasNext())) { if (rightGetNext) rightNextDefinedSegment = rightIter.next(); rightGetNext = false; - if (rightNextDefinedSegment.interval().start.shorterThan(startTime) || (rightNextDefinedSegment.interval().start.isEqualTo(startTime) && !rightNextDefinedSegment.interval().startInclusivity.moreRestrictiveThan(startInclusivity))) { + if (rightNextDefinedSegment.interval().start.shorterThan(startTime) || (rightNextDefinedSegment.interval().start.equals(startTime) && !rightNextDefinedSegment.interval().startInclusivity.moreRestrictiveThan(startInclusivity))) { rightInterval = rightNextDefinedSegment.interval(); rightValue = Optional.of(rightNextDefinedSegment.value()); rightGetNext = true; @@ -363,7 +362,7 @@ IntervalMap map2( rightValue = Optional.empty(); } - if (leftInterval.end.isEqualTo(rightInterval.end)) { + if (leftInterval.end.equals(rightInterval.end)) { endTime = leftInterval.end; if (leftInterval.includesEnd() && rightInterval.includesEnd()) { endInclusivity = Inclusive; 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 01768d6670..087815118a 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 @@ -268,7 +268,7 @@ public static > Resource shift(Resource resource, if (interval.shorterThan(ZERO)) { throw new IllegalArgumentException("Cannot shift resource by negative interval: " + interval); } - if (interval.isEqualTo(ZERO)) { + if (interval.equals(ZERO)) { return resource; } var cell = resource(initialDynamics); diff --git a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/Polynomial.java b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/Polynomial.java index 6390a5c531..584a2b92ef 100644 --- a/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/Polynomial.java +++ b/contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/modeling/polynomial/Polynomial.java @@ -10,15 +10,12 @@ import org.apache.commons.math3.complex.Complex; import java.util.Arrays; -import java.util.Optional; import java.util.function.BiPredicate; -import java.util.function.DoublePredicate; import java.util.function.Predicate; import java.util.stream.Stream; import static gov.nasa.jpl.aerie.contrib.streamline.core.Expiring.expiring; import static gov.nasa.jpl.aerie.contrib.streamline.core.Expiry.NEVER; -import static gov.nasa.jpl.aerie.contrib.streamline.core.Expiry.expiry; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.EPSILON; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.SECOND; import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.Discrete.discrete; @@ -64,7 +61,7 @@ public Double extract() { @Override public Polynomial step(Duration t) { - return t.isEqualTo(ZERO) ? this : polynomial(shift(coefficients(), t.ratioOver(SECOND))); + return t.equals(ZERO) ? this : polynomial(shift(coefficients(), t.ratioOver(SECOND))); } public int degree() { diff --git a/examples/foo-missionmodel/src/test/java/gov/nasa/jpl/aerie/foomissionmodel/FooSimulationDuplicationTest.java b/examples/foo-missionmodel/src/test/java/gov/nasa/jpl/aerie/foomissionmodel/FooSimulationDuplicationTest.java index 4da242a48e..534e2a3a64 100644 --- a/examples/foo-missionmodel/src/test/java/gov/nasa/jpl/aerie/foomissionmodel/FooSimulationDuplicationTest.java +++ b/examples/foo-missionmodel/src/test/java/gov/nasa/jpl/aerie/foomissionmodel/FooSimulationDuplicationTest.java @@ -333,7 +333,7 @@ private static Pair activityFrom(final D static void assertResultsEqual(SimulationResults expected, SimulationResults actual) { if (expected.equals(actual)) return; final var differences = new ArrayList(); - if (!expected.duration.isEqualTo(actual.duration)) { + if (!expected.duration.equals(actual.duration)) { differences.add("duration"); } if (!expected.realProfiles.equals(actual.realProfiles)) { @@ -351,7 +351,7 @@ static void assertResultsEqual(SimulationResults expected, SimulationResults act if (!expected.startTime.equals(actual.startTime)) { differences.add("startTime"); } - if (!expected.duration.isEqualTo(actual.duration)) { + if (!expected.duration.equals(actual.duration)) { differences.add("duration"); } if (!expected.topics.equals(actual.topics)) { diff --git a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/CheckpointSimulationDriver.java b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/CheckpointSimulationDriver.java index 4ec5787a90..b49ce2086a 100644 --- a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/CheckpointSimulationDriver.java +++ b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/CheckpointSimulationDriver.java @@ -106,7 +106,7 @@ public static Function desiredCheckpoints(final List checkpointAtEnd(Function stoppingCondition) { - return simulationState -> stoppingCondition.apply(simulationState) || simulationState.nextTime.isEqualTo(MAX_VALUE); + return simulationState -> stoppingCondition.apply(simulationState) || simulationState.nextTime.equals(MAX_VALUE); } private static Map getMinimumStartTimes( @@ -374,7 +374,7 @@ private static Map scheduleActivities( } Duration computedStartTime = offset; if (predecessor != null) { - computedStartTime = (curTime.isEqualTo(Duration.MIN_VALUE) ? Duration.ZERO : curTime).plus(offset); + computedStartTime = (curTime.equals(Duration.MIN_VALUE) ? Duration.ZERO : curTime).plus(offset); } final var taskId = engine.scheduleTask( computedStartTime, diff --git a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/SimulationResults.java b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/SimulationResults.java index 3cc0b6431d..9eadcbcf80 100644 --- a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/SimulationResults.java +++ b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/SimulationResults.java @@ -64,7 +64,7 @@ public boolean equals(final Object o) { if (!(o instanceof SimulationResults that)) return false; return startTime.equals(that.startTime) - && duration.isEqualTo(that.duration) + && duration.equals(that.duration) && realProfiles.equals(that.realProfiles) && discreteProfiles.equals(that.discreteProfiles) && simulatedActivities.equals(that.simulatedActivities) diff --git a/merlin-framework/src/main/java/gov/nasa/jpl/aerie/merlin/framework/Condition.java b/merlin-framework/src/main/java/gov/nasa/jpl/aerie/merlin/framework/Condition.java index 3be8ec907a..466e7bb2bb 100644 --- a/merlin-framework/src/main/java/gov/nasa/jpl/aerie/merlin/framework/Condition.java +++ b/merlin-framework/src/main/java/gov/nasa/jpl/aerie/merlin/framework/Condition.java @@ -53,14 +53,14 @@ static Condition and(final Condition left, final Condition right) { right$ = right.nextSatisfied(true, atEarliest, atLatest); if (right$.isEmpty()) break; - if (right$.get().isEqualTo(left$.get())) return left$; + if (right$.get().equals(left$.get())) return left$; atEarliest = right$.get(); if (atLatest.shorterThan(atEarliest)) break; left$ = left.nextSatisfied(true, atEarliest, atLatest); if (left$.isEmpty()) break; - if (left$.get().isEqualTo(right$.get())) return right$; + if (left$.get().equals(right$.get())) return right$; } return Optional.empty(); diff --git a/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java b/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java index 06d30a0689..712988c27c 100644 --- a/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java +++ b/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java @@ -602,11 +602,6 @@ public boolean isZero() { return this.micros == 0; } - public boolean isEqualTo(final Duration other) { - if(other == null) return false; - return this.micros == other.micros; - } - public static Duration parseISO8601(final String iso8601String) { final var javaDuration = java.time.Duration.parse(iso8601String); return microseconds(javaDuration.getSeconds() * 1000000L + javaDuration.getNano() / 1000L); diff --git a/orchestration-utils/src/main/java/gov/nasa/jpl/aerie/orchestration/simulation/SimulationExtentConsumer.java b/orchestration-utils/src/main/java/gov/nasa/jpl/aerie/orchestration/simulation/SimulationExtentConsumer.java index b997b1444d..283d28b819 100644 --- a/orchestration-utils/src/main/java/gov/nasa/jpl/aerie/orchestration/simulation/SimulationExtentConsumer.java +++ b/orchestration-utils/src/main/java/gov/nasa/jpl/aerie/orchestration/simulation/SimulationExtentConsumer.java @@ -21,7 +21,7 @@ public SimulationExtentConsumer(final long periodMillis) { @Override public void run() { // Only print if simulation time has progressed. - if(!lastAcceptedDuration.isEqualTo(lastReportedDuration)) { + if(!lastAcceptedDuration.equals(lastReportedDuration)) { System.out.println("Current simulation time: " + lastAcceptedDuration); lastReportedDuration = lastAcceptedDuration; } diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/EquationSolvingAlgorithms.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/EquationSolvingAlgorithms.java index 2bd318f5cd..4c98bc32dc 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/EquationSolvingAlgorithms.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/EquationSolvingAlgorithms.java @@ -96,7 +96,7 @@ public static class SecantDurationAlgorithm implements SecantAlgorithm private Duration chooseRandomX(final Duration bound1, final Duration bound2){ var low = bound1; var high = bound2; - if(low.isEqualTo(high)) return low; + if(low.equals(high)) return low; if(bound1.longerThan(bound2)) { low = bound2; high = bound1; } return Duration.of( randomGenerator.nextLong(low.in(Duration.MICROSECONDS), high.in(Duration.MICROSECONDS)), @@ -281,7 +281,7 @@ public RootFindingResult findRoot( x_n_double = x_n_double - (ff_x_nminus1.in(Duration.MICROSECONDS) / localDerivative); x_nminus1 = x_n; x_n = Duration.of((long) x_n_double, Duration.MICROSECONDS); - if (x_n.isEqualTo(x_nminus1)) throw new InfiniteDerivativeException(); + if (x_n.equals(x_nminus1)) throw new InfiniteDerivativeException(); final var resultXn = nextValueAt(f, x_n, xLow, xHigh, history, maxNbIterations - nbItPerformed); nbItPerformed += resultXn.nbIterationsPerformed(); ff_x_n = resultXn.result().fx(); diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/SchedulingActivity.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/SchedulingActivity.java index 114df90155..aee1d433e5 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/SchedulingActivity.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/SchedulingActivity.java @@ -247,8 +247,8 @@ public String toString() { */ public boolean equalsInProperties(final SchedulingActivity that){ return type.equals(that.type) - && duration.isEqualTo(that.duration) - && startOffset.isEqualTo(that.startOffset) + && duration.equals(that.duration) + && startOffset.equals(that.startOffset) && arguments.equals(that.arguments) && Objects.equals(topParent, that.topParent) && Objects.equals(anchorId, that.anchorId) diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/InMemoryCachedEngineStore.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/InMemoryCachedEngineStore.java index 68aa703a3f..6778fa0588 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/InMemoryCachedEngineStore.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/InMemoryCachedEngineStore.java @@ -101,7 +101,7 @@ private boolean shouldWeSave(final CachedSimulationEngine engine, for(final var cached: cachedEngines.entrySet()){ final var savedEngine = cached.getKey(); final var metadata = cached.getValue(); - if(engine.endsAt().isEqualTo(savedEngine.endsAt()) && + if(engine.endsAt().equals(savedEngine.endsAt()) && engine.activityDirectives().equals(savedEngine.activityDirectives()) && metadata.configuration.equals(configuration)){ return false; diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java index 7065abd55f..3d5fdec293 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/solver/PrioritySolver.java @@ -115,7 +115,7 @@ public Optional, Opt @Override public boolean alreadyVisited(final Duration x) { for(final var event:events){ - if(event.getLeft().x().isEqualTo(x)) return true; + if(event.getLeft().x().equals(x)) return true; } return false; } @@ -758,7 +758,7 @@ private ConflictSolverResult solveActivityTemplateConflict( + "). Missing cardinality: " + cardinalityLeft + ", duration: " - + (durationLeft.isEqualTo(ZERO) ? "N/A" : durationLeft)); + + (durationLeft.equals(ZERO) ? "N/A" : durationLeft)); final var newActivity = getBestNewActivity(missingActivityTemplateConflict); assert newActivity != null; //add the activities to the output plan diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/RootfindingTest.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/RootfindingTest.java index 4f21808ab6..00c3e2f807 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/RootfindingTest.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/RootfindingTest.java @@ -73,7 +73,7 @@ public Duration valueAt( final EquationSolvingAlgorithms.History historyType) throws EquationSolvingAlgorithms.DiscontinuityException { - if (x.isEqualTo(oneSecond)) { + if (x.equals(oneSecond)) { throw new EquationSolvingAlgorithms.DiscontinuityException(); } final var ret = x.times(2); diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacadeTest.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacadeTest.java index 6bf447e4b0..92d213c63a 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacadeTest.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/simulation/CheckpointSimulationFacadeTest.java @@ -67,7 +67,7 @@ public void simulateUntilTime() throws SimulationFacade.SimulationException, Sch final var plan = makePlanA012(activityTypes); newSimulationFacade.simulateNoResults(plan, t2hr); //we are stopping at 2hr, at the start of the last activity so it will not have a duration in the plan - assertNull(plan.getActivities().stream().filter(a -> a.startOffset().isEqualTo(t2hr)).findFirst().get().duration()); + assertNull(plan.getActivities().stream().filter(a -> a.startOffset().equals(t2hr)).findFirst().get().duration()); } /** diff --git a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java index 29f6c386fb..2596655641 100644 --- a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java +++ b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java @@ -485,7 +485,7 @@ export default () => Goal.CoexistenceGoal({ assertEquals(1, goalResult.satisfyingActivities().size()); final var activityCreated = results.updatedPlan .stream() - .filter(a -> a.startOffset().isEqualTo(Duration.MINUTES.times(55))) + .filter(a -> a.startOffset().equals(Duration.MINUTES.times(55))) .collect(Collectors.toSet()); assertEquals(1, activityCreated.size()); assertEquals(new SerializedValue.StringValue("Company"), activityCreated.iterator().next().serializedActivity().getArguments().get("producer")); @@ -1073,7 +1073,7 @@ export default () => Goal.CoexistenceGoal({ assertEquals(SerializedValue.of(1), growBanana.serializedActivity().getArguments().get("quantity")); // Checking both activities start at the same time - assertTrue(durativeActivity.startOffset().isEqualTo(growBanana.startOffset())); + assertTrue(durativeActivity.startOffset().equals(growBanana.startOffset())); // Checking both activities end at the same time final var activitytype = results.plan.getActivitiesByType().keySet().stream().filter(w->w.getName().equals("DurationParameterActivity")).findFirst().get(); @@ -1130,7 +1130,7 @@ export default () => Goal.CoexistenceGoal({ assertEquals(SerializedValue.of(1), growBanana.serializedActivity().getArguments().get("quantity")); // Checking start of peelBanana corresponds to end of growBanana - assertTrue(peelBanana.startOffset().isEqualTo(growBanana.startOffset().plus(growBananaDuration))); + assertTrue(peelBanana.startOffset().equals(growBanana.startOffset().plus(growBananaDuration))); } /** @@ -1350,7 +1350,7 @@ export default () => Goal.CoexistenceGoal({ final var activitytype = results.plan.getActivitiesByType().keySet().stream().filter(w->w.getName().equals("DurationParameterActivity")).findFirst(); if (activitytype.isEmpty()) fail("Could not find Coexistence Goal activity type"); - assertTrue(durationParameterActivity.startOffset().plus(results.plan.getActivitiesByType().get(activitytype.get()).get(0).duration()).isEqualTo(growBanana.startOffset().plus(growBananaDuration))); + assertTrue(durationParameterActivity.startOffset().plus(results.plan.getActivitiesByType().get(activitytype.get()).get(0).duration()).equals(growBanana.startOffset().plus(growBananaDuration))); } /** From fade718ebca85c01ce616b9e56528e934131e0a6 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Thu, 5 Sep 2024 17:23:28 -0700 Subject: [PATCH 086/108] remove sim control example in SampleProcedure --- .../examples/fooprocedures/procedures/SampleProcedure.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java index 8e323ace0b..2e75c80566 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SampleProcedure.java @@ -26,7 +26,5 @@ public void run(@NotNull final EditablePlan plan) { currentTime = currentTime.plus(step); } plan.commit(); -// var results = plan.simulate(new SimulateOptions()); -// var size = results.instances().collect().size(); } } From ea48982fe8d38e60fe344d80525bf77210f1028a Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Thu, 5 Sep 2024 17:36:05 -0700 Subject: [PATCH 087/108] Move args to goal invocationr record and represent them as Maps --- .../jpl/aerie/scheduler/goals/Procedure.java | 7 +-- .../server/http/SchedulerParsers.java | 16 +++++++ .../server/models/GoalInvocationRecord.java | 12 +++++ .../scheduler/server/models/GoalRecord.java | 10 ---- .../scheduler/server/models/GoalType.java | 3 +- .../server/models/SchedulingDSL.java | 2 +- .../server/models/Specification.java | 2 +- .../remotes/SpecificationRepository.java | 4 +- .../postgres/GetSchedulingGoalAction.java | 13 ++--- .../postgres/GetSpecificationGoalsAction.java | 47 ++++++++++++------- .../PostgresSpecificationRepository.java | 7 +-- ...teSchedulingGoalParameterSchemaAction.java | 6 +-- .../server/services/SpecificationService.java | 9 +--- .../services/SynchronousSchedulerAgent.java | 30 ++---------- .../services/MockSpecificationRepository.java | 4 +- .../SchedulingEdslIntegrationTests.java | 6 +-- 16 files changed, 84 insertions(+), 94 deletions(-) create mode 100644 scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalInvocationRecord.java delete mode 100644 scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalRecord.java diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java index a4ed23538d..42dde11280 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java @@ -19,15 +19,16 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.function.Function; import static gov.nasa.jpl.aerie.scheduler.plan.InMemoryEditablePlan.toSchedulingActivity; public class Procedure extends Goal { private final Path jarPath; - private final SerializedValue args; + private final Map args; - public Procedure(final PlanningHorizon planningHorizon, Path jarPath, SerializedValue args, boolean simulateAfter) { + public Procedure(final PlanningHorizon planningHorizon, Path jarPath, Map args, boolean simulateAfter) { this.simulateAfter = simulateAfter; this.planHorizon = planningHorizon; this.jarPath = jarPath; @@ -57,7 +58,7 @@ public void run(Evaluation eval, Plan plan, MissionModel missionModel, Functi lookupActivityType::apply ); - procedureMapper.deserialize(this.args).run(editablePlan); + procedureMapper.deserialize(SerializedValue.of(this.args)).run(editablePlan); if (!editablePlan.getUncommittedChanges().isEmpty()) { throw new IllegalStateException("procedural goal %s had changes that were not committed or rolled back".formatted(jarPath.getFileName())); diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerParsers.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerParsers.java index 260a846a41..cad88bc85f 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerParsers.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/SchedulerParsers.java @@ -8,6 +8,10 @@ import gov.nasa.jpl.aerie.types.MissionModelId; import gov.nasa.jpl.aerie.types.Timestamp; +import javax.json.Json; +import javax.json.stream.JsonParsingException; +import java.io.StringReader; +import java.util.List; import java.util.Optional; import static gov.nasa.jpl.aerie.json.BasicParsers.anyP; @@ -121,4 +125,16 @@ private static JsonParser> hasura .map( untuple(HasuraAction.HasuraSchedulingGoalEvent::new), $ -> tuple($.goalId(), $.revision())); + + public static T parseJson(final String jsonStr, final JsonParser parser) + throws InvalidJsonException, InvalidEntityException + { + try (final var reader = Json.createReader(new StringReader(jsonStr))) { + final var requestJson = reader.readValue(); + final var result = parser.parse(requestJson); + return result.getSuccessOrThrow(reason -> new InvalidEntityException(List.of(reason))); + } catch (JsonParsingException e) { + throw new InvalidJsonException(e); + } + } } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalInvocationRecord.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalInvocationRecord.java new file mode 100644 index 0000000000..bb048ddbf8 --- /dev/null +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalInvocationRecord.java @@ -0,0 +1,12 @@ +package gov.nasa.jpl.aerie.scheduler.server.models; + +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; + +import java.util.Map; + +public record GoalInvocationRecord( + GoalId id, + String name, + GoalType type, + Map args, + boolean simulateAfter) {} diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalRecord.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalRecord.java deleted file mode 100644 index 94bbbf1828..0000000000 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalRecord.java +++ /dev/null @@ -1,10 +0,0 @@ -package gov.nasa.jpl.aerie.scheduler.server.models; - -import java.nio.file.Path; -import java.util.Optional; - -public record GoalRecord( - GoalId id, - String name, - GoalType type, - boolean simulateAfter) {} diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java index 0aa89e456a..c4381a884e 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/GoalType.java @@ -1,9 +1,8 @@ package gov.nasa.jpl.aerie.scheduler.server.models; -import javax.json.JsonObject; import java.nio.file.Path; public sealed interface GoalType { record EDSL(GoalSource source) implements GoalType {} - record JAR(Path path, String args) implements GoalType {} + record JAR(Path path) implements GoalType {} } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/SchedulingDSL.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/SchedulingDSL.java index 45a65d30b8..37e3907d51 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/SchedulingDSL.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/SchedulingDSL.java @@ -302,7 +302,7 @@ record GoalApplyWhen( ) implements GoalSpecifier {} record Procedure( Path jarPath, - SerializedValue arguments + Map arguments ) implements GoalSpecifier {} } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/Specification.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/Specification.java index 42d471e34f..2c034c9d32 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/Specification.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/models/Specification.java @@ -15,6 +15,6 @@ public record Specification( Timestamp horizonEndTimestamp, Map simulationArguments, boolean analysisOnly, - List goalsByPriority, + List goalsByPriority, List schedulingConditions ) {} diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/SpecificationRepository.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/SpecificationRepository.java index 1cb1436f77..8c3d192eea 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/SpecificationRepository.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/SpecificationRepository.java @@ -5,7 +5,7 @@ import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSpecificationException; import gov.nasa.jpl.aerie.scheduler.server.exceptions.SpecificationLoadException; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; -import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import gov.nasa.jpl.aerie.scheduler.server.models.Specification; import gov.nasa.jpl.aerie.scheduler.server.models.SpecificationId; import gov.nasa.jpl.aerie.scheduler.server.remotes.postgres.SpecificationRevisionData; @@ -15,6 +15,6 @@ public interface SpecificationRepository { Specification getSpecification(SpecificationId specificationId) throws NoSuchSpecificationException, SpecificationLoadException; SpecificationRevisionData getSpecificationRevisionData(SpecificationId specificationId) throws NoSuchSpecificationException; - GoalRecord getGoal(GoalId goalId) throws NoSuchSchedulingGoalException; + GoalType getGoal(GoalId goalId) throws NoSuchSchedulingGoalException; void updateGoalParameterSchema(GoalId goalId, ValueSchema schema); } diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java index 4e40e1a9f4..40c8b1ee0b 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSchedulingGoalAction.java @@ -1,7 +1,6 @@ package gov.nasa.jpl.aerie.scheduler.server.remotes.postgres; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; -import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; import gov.nasa.jpl.aerie.scheduler.server.models.GoalSource; import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import org.intellij.lang.annotations.Language; @@ -14,7 +13,7 @@ /*package-local*/ final class GetSchedulingGoalAction implements AutoCloseable { private final @Language("SQL") String sql = """ - select gd.goal_id, gd.revision, gm.name, gd.definition, gd.type, encode(f.path, 'escape') as path + select gd.definition, gd.type, encode(f.path, 'escape') as path from scheduler.scheduling_goal_definition gd left join scheduler.scheduling_goal_metadata gm on gd.goal_id = gm.id left join merlin.uploaded_file f on gd.uploaded_jar_id = f.id @@ -27,24 +26,18 @@ public GetSchedulingGoalAction(final Connection connection) throws SQLException this.statement = connection.prepareStatement(sql); } - public Optional get(final GoalId goalId) throws SQLException { + public Optional get(final GoalId goalId) throws SQLException { this.statement.setLong(1, goalId.id()); this.statement.setLong(2, goalId.revision()); final var resultSet = this.statement.executeQuery(); if (!resultSet.next()) return Optional.empty(); - final var name = resultSet.getString("name"); final var definition = resultSet.getString("definition"); final var type = resultSet.getString("type"); final var path = resultSet.getString("path"); - return Optional.of(new GoalRecord( - goalId, - name, - type.equals("JAR") ? new GoalType.JAR(Path.of(path), "" /* TODO this is a property of the specification, not the goal */) : new GoalType.EDSL(new GoalSource(definition)), - true // TODO this is not a property of the goal, but rather of the specification - )); + return Optional.of(type.equals("JAR") ? new GoalType.JAR(Path.of(path)) : new GoalType.EDSL(new GoalSource(definition))); } @Override diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java index ec6f14f762..d6968b78be 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java @@ -1,7 +1,10 @@ package gov.nasa.jpl.aerie.scheduler.server.remotes.postgres; +import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser; +import gov.nasa.jpl.aerie.scheduler.server.http.InvalidEntityException; +import gov.nasa.jpl.aerie.scheduler.server.http.InvalidJsonException; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; -import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalInvocationRecord; import gov.nasa.jpl.aerie.scheduler.server.models.GoalSource; import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import org.intellij.lang.annotations.Language; @@ -14,6 +17,8 @@ import java.util.List; import java.util.Optional; +import static gov.nasa.jpl.aerie.scheduler.server.http.SchedulerParsers.parseJson; + /*package-local*/ final class GetSpecificationGoalsAction implements AutoCloseable { private final @Language("SQL") String sql = """ select s.goal_id, gd.revision, gm.name, gd.definition, s.goal_invocation_id, s.simulate_after, gd.type, encode(f.path, 'escape') as path, s.arguments @@ -37,30 +42,36 @@ public GetSpecificationGoalsAction(final Connection connection) throws SQLExcept this.statement = connection.prepareStatement(sql); } - public List get(final long specificationId) throws SQLException { + public List get(final long specificationId) throws SQLException { this.statement.setLong(1, specificationId); final var resultSet = this.statement.executeQuery(); - final var goals = new ArrayList(); - while (resultSet.next()) { - final var id = resultSet.getLong("goal_id"); - final var goalInvocationId = resultSet.getLong("goal_invocation_id"); - final var revision = resultSet.getLong("revision"); - final var name = resultSet.getString("name"); - final var definition = resultSet.getString("definition"); - final var simulateAfter = resultSet.getBoolean("simulate_after"); - final var type = resultSet.getString("type"); - final var path = resultSet.getString("path"); - final var args = resultSet.getString("arguments"); - goals.add(new GoalRecord( + try { + + final var goals = new ArrayList(); + while (resultSet.next()) { + final var id = resultSet.getLong("goal_id"); + final var goalInvocationId = resultSet.getLong("goal_invocation_id"); + final var revision = resultSet.getLong("revision"); + final var name = resultSet.getString("name"); + final var definition = resultSet.getString("definition"); + final var simulateAfter = resultSet.getBoolean("simulate_after"); + final var type = resultSet.getString("type"); + final var path = resultSet.getString("path"); + final var args = parseJson(resultSet.getString("arguments"), new SerializedValueJsonParser()); + + goals.add(new GoalInvocationRecord( new GoalId(id, revision, Optional.of(goalInvocationId)), name, - type.equals("JAR") ? new GoalType.JAR(Path.of(path), args) : new GoalType.EDSL(new GoalSource(definition)), + type.equals("JAR") ? new GoalType.JAR(Path.of(path)) : new GoalType.EDSL(new GoalSource(definition)), + args.asMap().get(), simulateAfter - )); + )); + } + return goals; + } catch (InvalidJsonException | InvalidEntityException e) { + throw new RuntimeException(e); } - - return goals; } @Override diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/PostgresSpecificationRepository.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/PostgresSpecificationRepository.java index 5f97edc0bc..3338460c64 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/PostgresSpecificationRepository.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/PostgresSpecificationRepository.java @@ -4,8 +4,9 @@ import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSchedulingGoalException; import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSpecificationException; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import gov.nasa.jpl.aerie.scheduler.server.models.SchedulingConditionRecord; -import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalInvocationRecord; import gov.nasa.jpl.aerie.scheduler.server.models.PlanId; import gov.nasa.jpl.aerie.scheduler.server.models.Specification; import gov.nasa.jpl.aerie.scheduler.server.models.SpecificationId; @@ -28,7 +29,7 @@ public Specification getSpecification(final SpecificationId specificationId) { final SpecificationRecord specificationRecord; final PlanId planId; - final List goals; + final List goals; final List schedulingConditions; try (final var connection = this.dataSource.getConnection(); final var getSpecificationAction = new GetSpecificationAction(connection); @@ -77,7 +78,7 @@ public SpecificationRevisionData getSpecificationRevisionData(final Specificatio } @Override - public GoalRecord getGoal(final GoalId goalId) throws NoSuchSchedulingGoalException { + public GoalType getGoal(final GoalId goalId) throws NoSuchSchedulingGoalException { try (final var connection = this.dataSource.getConnection()) { try (final var getGoalAction = new GetSchedulingGoalAction(connection)) { return getGoalAction diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java index 579291ad5d..27dce53309 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/UpdateSchedulingGoalParameterSchemaAction.java @@ -3,20 +3,16 @@ import gov.nasa.jpl.aerie.merlin.driver.json.ValueSchemaJsonParser; import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; -import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; -import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import org.intellij.lang.annotations.Language; -import java.nio.file.Path; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; -import java.util.Optional; /*package-local*/ final class UpdateSchedulingGoalParameterSchemaAction implements AutoCloseable { private final @Language("SQL") String sql = """ update scheduler.scheduling_goal_definition gd - set parameter_schema=?::jsonb + set parameter_schema=?::jsonb where gd.goal_id = ? and gd.revision = ?; """; diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java index 63c06b0efd..9fe6fad93e 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java @@ -1,15 +1,11 @@ package gov.nasa.jpl.aerie.scheduler.server.services; -import gov.nasa.jpl.aerie.merlin.driver.DirectiveTypeRegistry; -import gov.nasa.jpl.aerie.merlin.driver.MissionModelLoader; -import gov.nasa.jpl.aerie.merlin.protocol.model.ModelType; import gov.nasa.ammos.aerie.procedural.scheduling.ProcedureMapper; import gov.nasa.jpl.aerie.scheduler.ProcedureLoader; import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSchedulingGoalException; import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSpecificationException; import gov.nasa.jpl.aerie.scheduler.server.exceptions.SpecificationLoadException; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; -import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import gov.nasa.jpl.aerie.scheduler.server.models.Specification; import gov.nasa.jpl.aerie.scheduler.server.models.SpecificationId; @@ -17,7 +13,6 @@ import gov.nasa.jpl.aerie.scheduler.server.remotes.postgres.SpecificationRevisionData; import java.nio.file.Path; -import java.util.HashMap; public record SpecificationService(SpecificationRepository specificationRepository) { // Queries @@ -34,13 +29,13 @@ public SpecificationRevisionData getSpecificationRevisionData(final Specificatio } public void refreshSchedulingProcedureParameterTypes(long goalId, long revision) { - final GoalRecord goal; + final GoalType goal; try { goal = specificationRepository.getGoal(new GoalId(goalId, revision)); } catch (NoSuchSchedulingGoalException e) { throw new RuntimeException(e); } - switch (goal.type()) { + switch (goal) { case GoalType.EDSL edsl -> { // Do nothing } diff --git a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java index 7d159ad975..5d046daeca 100644 --- a/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java +++ b/scheduler-worker/src/main/java/gov/nasa/jpl/aerie/scheduler/worker/services/SynchronousSchedulerAgent.java @@ -3,7 +3,6 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; @@ -21,12 +20,10 @@ import java.util.jar.JarFile; import java.util.stream.Collectors; -import gov.nasa.jpl.aerie.json.JsonParser; import gov.nasa.jpl.aerie.merlin.driver.MissionModel; import gov.nasa.jpl.aerie.merlin.driver.MissionModelLoader; import gov.nasa.jpl.aerie.merlin.driver.SimulationEngineConfiguration; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; -import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser; import gov.nasa.jpl.aerie.merlin.protocol.model.SchedulerModel; import gov.nasa.jpl.aerie.merlin.protocol.model.SchedulerPlugin; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; @@ -47,13 +44,12 @@ import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSpecificationException; import gov.nasa.jpl.aerie.scheduler.server.exceptions.ResultsProtocolFailure; import gov.nasa.jpl.aerie.scheduler.server.exceptions.SpecificationLoadException; -import gov.nasa.jpl.aerie.scheduler.server.http.InvalidEntityException; import gov.nasa.jpl.aerie.scheduler.server.http.InvalidJsonException; import gov.nasa.jpl.aerie.scheduler.server.http.ResponseSerializers; import gov.nasa.jpl.aerie.scheduler.server.models.DatasetId; import gov.nasa.jpl.aerie.scheduler.server.models.ExternalProfiles; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; -import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalInvocationRecord; import gov.nasa.jpl.aerie.scheduler.server.models.GoalSource; import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import gov.nasa.jpl.aerie.scheduler.server.models.MerlinPlan; @@ -80,9 +76,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.json.Json; -import javax.json.stream.JsonParsingException; - /** * agent that handles posed scheduling requests by blocking the requester thread until scheduling is complete * @@ -194,7 +187,7 @@ public void schedule( final var orderedGoals = new ArrayList(); final var goals = new HashMap(); - final var compiledGoals = new ArrayList>(); + final var compiledGoals = new ArrayList>(); final var failedGoals = new ArrayList>>(); for (final var goalRecord : specification.goalsByPriority()) { switch (goalRecord.type()) { @@ -216,12 +209,7 @@ public void schedule( } } case GoalType.JAR jar -> { - try { - final var serializedValue = parseJson(jar.args(), new SerializedValueJsonParser()); - compiledGoals.add(Pair.of(goalRecord, new SchedulingDSL.GoalSpecifier.Procedure(modelJarsDir.resolve(jar.path()), serializedValue))); - } catch (InvalidJsonException | InvalidEntityException e) { - throw new RuntimeException(e); - } + compiledGoals.add(Pair.of(goalRecord, new SchedulingDSL.GoalSpecifier.Procedure(modelJarsDir.resolve(jar.path()), goalRecord.args()))); } } } @@ -661,16 +649,4 @@ private ScheduleResults collectResults(final Plan plan, Map T parseJson(final String jsonStr, final JsonParser parser) - throws InvalidJsonException, InvalidEntityException - { - try (final var reader = Json.createReader(new StringReader(jsonStr))) { - final var requestJson = reader.readValue(); - final var result = parser.parse(requestJson); - return result.getSuccessOrThrow(reason -> new InvalidEntityException(List.of(reason))); - } catch (JsonParsingException e) { - throw new InvalidJsonException(e); - } - } } diff --git a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockSpecificationRepository.java b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockSpecificationRepository.java index 058266be50..9547dbc82a 100644 --- a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockSpecificationRepository.java +++ b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockSpecificationRepository.java @@ -6,7 +6,7 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSpecificationException; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; -import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalType; import gov.nasa.jpl.aerie.scheduler.server.models.Specification; import gov.nasa.jpl.aerie.scheduler.server.models.SpecificationId; import gov.nasa.jpl.aerie.scheduler.server.remotes.SpecificationRepository; @@ -38,7 +38,7 @@ public SpecificationRevisionData getSpecificationRevisionData(final Specificatio } @Override - public GoalRecord getGoal(final GoalId goalId) { + public GoalType getGoal(final GoalId goalId) { return null; } diff --git a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java index 2596655641..b7dda0af9a 100644 --- a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java +++ b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingEdslIntegrationTests.java @@ -43,7 +43,7 @@ import gov.nasa.jpl.aerie.scheduler.server.models.SchedulingConditionRecord; import gov.nasa.jpl.aerie.scheduler.server.models.SchedulingConditionSource; import gov.nasa.jpl.aerie.scheduler.server.models.GoalId; -import gov.nasa.jpl.aerie.scheduler.server.models.GoalRecord; +import gov.nasa.jpl.aerie.scheduler.server.models.GoalInvocationRecord; import gov.nasa.jpl.aerie.scheduler.server.models.GoalSource; import gov.nasa.jpl.aerie.scheduler.server.models.PlanId; import gov.nasa.jpl.aerie.scheduler.server.models.ResourceType; @@ -2195,10 +2195,10 @@ private SchedulingRunResults runScheduler( mockMerlinService.setPlanningHorizon(planningHorizon); externalProfiles.ifPresent(mockMerlinService::setExternalDataset); final var planId = new PlanId(1L); - final var goalsByPriority = new ArrayList(); + final var goalsByPriority = new ArrayList(); for (final var goal : goals) { - goalsByPriority.add(new GoalRecord(goal.goalId(), "test goal", new GoalType.EDSL(new GoalSource(goal.definition())), goal.simulateAfter())); + goalsByPriority.add(new GoalInvocationRecord(goal.goalId(), "test goal", new GoalType.EDSL(new GoalSource(goal.definition())), Map.of(), goal.simulateAfter())); } final var specificationService = new SpecificationService(new MockSpecificationRepository(Map.of(new SpecificationId(1L), new Specification( new SpecificationId(1L), From a274776829faeba06e06a5d8c907be7cfb553704 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Thu, 5 Sep 2024 17:50:19 -0700 Subject: [PATCH 088/108] remove shadow plugin, not necessary once published --- procedural/constraints/build.gradle | 1 - procedural/processor/build.gradle | 1 - procedural/remote/build.gradle | 1 - procedural/scheduling/build.gradle | 1 - procedural/timeline/build.gradle | 1 - 5 files changed, 5 deletions(-) diff --git a/procedural/constraints/build.gradle b/procedural/constraints/build.gradle index 2749cc1aeb..5766d38999 100644 --- a/procedural/constraints/build.gradle +++ b/procedural/constraints/build.gradle @@ -2,7 +2,6 @@ import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { - id 'com.gradleup.shadow' version '8.3.0' id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' diff --git a/procedural/processor/build.gradle b/procedural/processor/build.gradle index 982f7d732f..2f24cb771e 100644 --- a/procedural/processor/build.gradle +++ b/procedural/processor/build.gradle @@ -1,7 +1,6 @@ plugins { id 'java-library' id 'maven-publish' - id 'com.gradleup.shadow' version '8.3.0' } java { diff --git a/procedural/remote/build.gradle b/procedural/remote/build.gradle index 00bade518b..17064d6e52 100644 --- a/procedural/remote/build.gradle +++ b/procedural/remote/build.gradle @@ -2,7 +2,6 @@ import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { - id 'com.gradleup.shadow' version '8.3.0' id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' diff --git a/procedural/scheduling/build.gradle b/procedural/scheduling/build.gradle index a52d345db8..b19c93fef6 100644 --- a/procedural/scheduling/build.gradle +++ b/procedural/scheduling/build.gradle @@ -2,7 +2,6 @@ import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { - id 'com.gradleup.shadow' version '8.3.0' id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' diff --git a/procedural/timeline/build.gradle b/procedural/timeline/build.gradle index 6e81b03dd5..e565f8cd43 100644 --- a/procedural/timeline/build.gradle +++ b/procedural/timeline/build.gradle @@ -2,7 +2,6 @@ import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { - id 'com.gradleup.shadow' version '8.3.0' id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.10' From 16bef1b1824be6afe00149de4d7c3580e40904ca Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Thu, 5 Sep 2024 17:50:37 -0700 Subject: [PATCH 089/108] remove unused imports and small misc changes --- .../src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java | 1 - 1 file changed, 1 deletion(-) diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java index 458b44f714..4a08b5ca41 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java @@ -140,7 +140,6 @@ void afterEach() throws IOException { hasura.deleteMissionModel(modelId); } - private void insertActivities() throws IOException { // Duration argument is specified on one but not the other to verify that the scheduler can pick up on effective args hasura.insertActivityDirective(planId, "GrowBanana", "1h", JsonValue.EMPTY_JSON_OBJECT); From d0637f8b333b46b3972e17f4f3205e2ec02daca2 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Thu, 5 Sep 2024 19:26:47 -0700 Subject: [PATCH 090/108] Upgrade dokka --- procedural/build.gradle | 2 +- procedural/constraints/build.gradle | 4 ++-- procedural/remote/build.gradle | 2 +- procedural/scheduling/build.gradle | 2 +- procedural/timeline/MODULE_DOCS.md | 3 +++ procedural/timeline/build.gradle | 6 ++---- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/procedural/build.gradle b/procedural/build.gradle index a1f34d94cc..a781f7aa15 100644 --- a/procedural/build.gradle +++ b/procedural/build.gradle @@ -1,6 +1,6 @@ plugins { id "org.jetbrains.kotlin.jvm" version "2.0.20" - id 'org.jetbrains.dokka' version '1.9.10' + id 'org.jetbrains.dokka' version '1.9.20' } subprojects { diff --git a/procedural/constraints/build.gradle b/procedural/constraints/build.gradle index 5766d38999..afcf3a3069 100644 --- a/procedural/constraints/build.gradle +++ b/procedural/constraints/build.gradle @@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' - id 'org.jetbrains.dokka' version '1.9.10' + id 'org.jetbrains.dokka' version '1.9.20' } repositories { @@ -44,7 +44,7 @@ dokkaHtmlPartial.configure { moduleName.set("Constraints") reportUndocumented.set(true) - failOnWarning.set(true) + failOnWarning.set(false) // contains descriptions for the module and the packages includes.from("MODULE_DOCS.md") diff --git a/procedural/remote/build.gradle b/procedural/remote/build.gradle index 17064d6e52..6703ac0627 100644 --- a/procedural/remote/build.gradle +++ b/procedural/remote/build.gradle @@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' - id 'org.jetbrains.dokka' version '1.9.10' + id 'org.jetbrains.dokka' version '1.9.20' } repositories { diff --git a/procedural/scheduling/build.gradle b/procedural/scheduling/build.gradle index b19c93fef6..0d67c94c69 100644 --- a/procedural/scheduling/build.gradle +++ b/procedural/scheduling/build.gradle @@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' - id 'org.jetbrains.dokka' version '1.9.10' + id 'org.jetbrains.dokka' version '1.9.20' id 'maven-publish' } diff --git a/procedural/timeline/MODULE_DOCS.md b/procedural/timeline/MODULE_DOCS.md index 4d77768689..d85f512215 100644 --- a/procedural/timeline/MODULE_DOCS.md +++ b/procedural/timeline/MODULE_DOCS.md @@ -106,3 +106,6 @@ Tools for querying simulation results, activity directives, and general informat # Package gov.nasa.ammos.aerie.procedural.timeline.util Common tools used by operations and timeline constructors to sanitize and process lists. +# Package gov.nasa.ammos.aerie.procedural.timeline.util.duration +Kotlin operator overloads for ergonomic Duration operations. + diff --git a/procedural/timeline/build.gradle b/procedural/timeline/build.gradle index e565f8cd43..086afad4a4 100644 --- a/procedural/timeline/build.gradle +++ b/procedural/timeline/build.gradle @@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' - id 'org.jetbrains.dokka' version '1.9.10' + id 'org.jetbrains.dokka' version '1.9.20' id 'maven-publish' } @@ -21,8 +21,6 @@ dependencies { testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - -// dokkaHtmlPlugin 'org.jetbrains.dokka:kotlin-as-java-plugin:1.9.10' } tasks.withType(KotlinJvmCompile.class).configureEach { @@ -44,7 +42,7 @@ dokkaHtmlPartial.configure { moduleName.set("Timeline") reportUndocumented.set(true) - failOnWarning.set(true) + failOnWarning.set(false) // contains descriptions for the module and the packages includes.from("MODULE_DOCS.md") From 60d99b7b9621a63feb1d2c586e3b233b8733e215 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Mon, 9 Sep 2024 08:26:39 -0700 Subject: [PATCH 091/108] Remove extra setLong call --- .../server/remotes/postgres/GetCreatedActivitiesAction.java | 1 - 1 file changed, 1 deletion(-) diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java index 642cc661d7..3b823488a0 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java @@ -33,7 +33,6 @@ public GetCreatedActivitiesAction(final Connection connection) throws SQLExcepti public Map> get(final long analysisId) throws SQLException { this.statement.setLong(1, analysisId); - this.statement.setLong(2, analysisId); final var resultSet = this.statement.executeQuery(); final var createdActivities = new HashMap>(); From 450d56820d968e5a17117adada63d7ea7e9a3e76 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Mon, 9 Sep 2024 08:58:23 -0700 Subject: [PATCH 092/108] remove `Optional.of` wrapping to implictly use constructor wrapper instead --- .../server/remotes/postgres/GetCreatedActivitiesAction.java | 2 +- .../server/remotes/postgres/GetSpecificationGoalsAction.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java index 3b823488a0..e675a6c73a 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetCreatedActivitiesAction.java @@ -40,7 +40,7 @@ public Map> get(final long analysisId) throws final var goalId = new GoalId( resultSet.getLong("goal_id"), resultSet.getLong("goal_revision"), - Optional.of(resultSet.getLong("goal_invocation_id")) + resultSet.getLong("goal_invocation_id") ); final var activityId = new ActivityDirectiveId(resultSet.getLong("activity_id")); diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java index d6968b78be..1f31b23fa2 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java @@ -61,7 +61,7 @@ public List get(final long specificationId) throws SQLExce final var args = parseJson(resultSet.getString("arguments"), new SerializedValueJsonParser()); goals.add(new GoalInvocationRecord( - new GoalId(id, revision, Optional.of(goalInvocationId)), + new GoalId(id, revision, goalInvocationId), name, type.equals("JAR") ? new GoalType.JAR(Path.of(path)) : new GoalType.EDSL(new GoalSource(definition)), args.asMap().get(), From 3b487a682b3a494b4ac2dbe0a3d672fac1fd2879 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Mon, 9 Sep 2024 09:26:02 -0700 Subject: [PATCH 093/108] introspect failed scheduling run reasons --- .../nasa/jpl/aerie/e2e/SchedulingTests.java | 4 ++- .../jpl/aerie/e2e/utils/HasuraRequests.java | 32 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java index 4a08b5ca41..1e1b1894c9 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java @@ -1063,7 +1063,9 @@ void proceduralUploadWorks() throws IOException { */ @Test void executeSchedulingRunWithoutArguments() throws IOException { - assertThrows(AssertionFailedError.class, () -> hasura.awaitScheduling(specId)); + final var resp = hasura.awaitFailingScheduling(specId); + final var message = resp.reason().getString("message"); + assertTrue(message.contains("java.lang.RuntimeException: Record missing key Component[name=quantity")); } /** diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java index 385dc3327e..0e98a0ef7a 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java @@ -608,6 +608,38 @@ public SchedulingResponse awaitScheduling(int schedulingSpecId, int timeout) thr throw new TimeoutError("Scheduling timed out after " + timeout + " seconds"); } + /** + * Run scheduling on the specified scheduling specification with a timeout of 30 seconds + * Used when the scheduling run is expected to fail. + */ + public SchedulingResponse awaitFailingScheduling(int schedulingSpecId) throws IOException { + return awaitFailingScheduling(schedulingSpecId, 30); + } + + /** + * Run scheduling on the specified scheduling specification + * Used when the scheduling run is expected to fail. + */ + public SchedulingResponse awaitFailingScheduling(int schedulingSpecId, int timeout) throws IOException { + for(int i = 0; i < timeout; ++i){ + final var response = schedule(schedulingSpecId); + switch (response.status()) { + case "pending", "incomplete" -> { + try { + Thread.sleep(1000); // 1s + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + case "complete", "failed" -> { + return response; + } + default -> fail("Scheduling returned bad status " + response.status() + " with reason " +response.reason()); + } + } + throw new TimeoutError("Scheduling timed out after " + timeout + " seconds"); + } + /** * Start and immediately cancel a scheduling run with a timeout of 30 seconds * @param schedulingSpecId the scheduling specification to use From b0c4fd2d23dfd524d5122537c4b8600ca9af8c59 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Mon, 9 Sep 2024 10:35:29 -0700 Subject: [PATCH 094/108] Publish kotlin docs to gh pages --- .github/workflows/deploy-to-gh-pages.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/deploy-to-gh-pages.yml b/.github/workflows/deploy-to-gh-pages.yml index 5af4fbd129..932a69b1f8 100644 --- a/.github/workflows/deploy-to-gh-pages.yml +++ b/.github/workflows/deploy-to-gh-pages.yml @@ -31,6 +31,8 @@ jobs: cp -a ./scheduler-worker/scheduling-dsl-compiler/build/docs ./build/scheduling-edsl-api - name: Build Java Docs run: ./gradlew javadoc + - name: Build Kotlin Docs + run: ./gradlew dokkaHtmlMultiModule - name: Copy Java Docs to Build Directory run: | cp -a ./constraints/build/docs/javadoc ./build/javadoc/constraints @@ -50,6 +52,7 @@ jobs: cp -a ./scheduler-driver/build/docs/javadoc ./build/javadoc/scheduler-driver cp -a ./scheduler-server/build/docs/javadoc ./build/javadoc/scheduler-server cp -a ./scheduler-worker/build/docs/javadoc ./build/javadoc/scheduler-worker + cp -a ./procedural/build/dokka/htmlMultiModule ./build/dokka/procedural-apis - name: Upload Artifact uses: actions/upload-pages-artifact@v3 with: From d230cd22484bf1b2effcf2e51da5ab8157185b14 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Mon, 9 Sep 2024 10:21:42 -0700 Subject: [PATCH 095/108] delete unused `ProcedureLoadingTest` class --- .../constraints/ProcedureLoadingTest.java | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoadingTest.java diff --git a/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoadingTest.java b/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoadingTest.java deleted file mode 100644 index 7db3ebfb05..0000000000 --- a/procedural/examples/foo-procedures/src/test/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ProcedureLoadingTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.constraints; - -import gov.nasa.ammos.aerie.procedural.constraints.Constraint; - -import java.nio.file.Path; - -public class ProcedureLoadingTest { - void foo() throws ProcedureLoader.ProcedureLoadException { - var jarPath = "/Users/dailis/projects/aerie/worktrees/develop/procedural/examples/foo-procedures/build/libs/foo-procedures-ConstFruit-constraint.jar"; - - // Load jar from absolute filepath - final Constraint constraint = ProcedureLoader.loadProcedure(Path.of(jarPath), "name", "version"); - // Run code - - } -} From a2a6835bb4b1ed049850fe88b2f29eebbde40bdb Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Mon, 9 Sep 2024 11:51:26 -0700 Subject: [PATCH 096/108] revert null check in GQL DB service anchoring --- .../scheduler/server/services/GraphQLMerlinDatabaseService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java index d3b9998302..e9795dedc8 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/GraphQLMerlinDatabaseService.java @@ -580,7 +580,7 @@ mutation createAllPlanActivityDirectives($activities: [activity_directive_insert .add("plan_id", planId.id()) .add("type", act.getType().getName()) .add("start_offset", act.startOffset().toString()) - .add("anchored_to_start", act.anchorId() == null || act.anchoredToStart()); + .add("anchored_to_start", act.anchoredToStart()); //add duration to parameters if controllable final var insertionObjectArguments = Json.createObjectBuilder(); From c2daa92cdd9c69d26f9e3a721c3011450111c063 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Mon, 9 Sep 2024 11:53:08 -0700 Subject: [PATCH 097/108] unique key on invocation id when serializing --- .../jpl/aerie/scheduler/server/http/ResponseSerializers.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/ResponseSerializers.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/ResponseSerializers.java index 566c1a6f8e..44302a40c2 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/ResponseSerializers.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/http/ResponseSerializers.java @@ -95,7 +95,7 @@ public static JsonValue serializeScheduleResults(final ScheduleResults results) .entrySet() .stream() .collect( - Collectors.toMap(e -> Long.toString(e.getKey().id()), Map.Entry::getValue))); + Collectors.toMap(e -> Long.toString(e.getKey().goalInvocationId().get()), Map.Entry::getValue))); } private static JsonValue serializeGoalResult(final ScheduleResults.GoalResult goalResult) { From 4559d168b8b63f4b55f667ab441e9ed98284cf1a Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Mon, 9 Sep 2024 12:01:01 -0700 Subject: [PATCH 098/108] call JSON parsing a SQLException so it gets serialized as DatabaseException --- .../server/remotes/postgres/GetSpecificationGoalsAction.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java index 1f31b23fa2..e74941e538 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/remotes/postgres/GetSpecificationGoalsAction.java @@ -13,6 +13,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.text.ParseException; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -70,7 +71,7 @@ public List get(final long specificationId) throws SQLExce } return goals; } catch (InvalidJsonException | InvalidEntityException e) { - throw new RuntimeException(e); + throw new SQLException(e); } } From 1de87766d7d5bea6c86502832edebf0ad60cd981 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Mon, 9 Sep 2024 12:08:34 -0700 Subject: [PATCH 099/108] add new procedural scheduling test cases --- .../nasa/jpl/aerie/e2e/SchedulingTests.java | 66 ++++++++++++++++++- .../gov/nasa/jpl/aerie/e2e/utils/GQL.java | 8 +++ .../jpl/aerie/e2e/utils/HasuraRequests.java | 14 +++- 3 files changed, 86 insertions(+), 2 deletions(-) diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java index 1e1b1894c9..7251b504b4 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/SchedulingTests.java @@ -130,7 +130,6 @@ void beforeEach() throws IOException, InterruptedException { "24:00:00", planStartTimestamp); schedulingSpecId = hasura.getSchedulingSpecId(planId); - // Unset directiveId vars } @AfterEach @@ -1092,5 +1091,70 @@ void executeSchedulingRunWithArguments() throws IOException { $ -> Objects.equals($.type(), "BiteBanana") && Objects.equals($.startOffset(), "30:00:00") )); } + + /** + * Run a spec with two invocations of the same procedure in it + */ + @Test + void executeMultipleInvocationsOfSameProcedure() throws IOException { + final var args = Json.createObjectBuilder().add("quantity", 2).build(); + hasura.updateSchedulingSpecGoalArguments(procedureId.invocationId(), args); + + final var secondInvocationId = hasura.insertGoalInvocation(procedureId.goalId(), specId); + hasura.updateSchedulingSpecGoalArguments(secondInvocationId.invocationId(), args); + + final var resp = hasura.awaitScheduling(specId); + + final var plan = hasura.getPlan(planId); + final var activities = plan.activityDirectives(); + + assertEquals(4, activities.size()); + } + + /** + * Run a spec with two procedures in it + */ + @Test + void executeMultipleProcedures() throws IOException { + final var args = Json.createObjectBuilder().add("quantity", 2).build(); + hasura.updateSchedulingSpecGoalArguments(procedureId.invocationId(), args); + + final var secondProcedure = hasura.createSchedulingSpecProcedure( + "Test Scheduling Procedure 2", + procedureJarId, + specId, + 1); + + hasura.updateSchedulingSpecGoalArguments(secondProcedure.invocationId(), args); + + final var resp = hasura.awaitScheduling(specId); + + final var plan = hasura.getPlan(planId); + final var activities = plan.activityDirectives(); + + assertEquals(4, activities.size()); + } + + /** + * Run a spec with one EDSL goal and one procedure + */ + @Test + void executeEDSLAndProcedure() throws IOException { + final var args = Json.createObjectBuilder().add("quantity", 4).build(); + hasura.updateSchedulingSpecGoalArguments(procedureId.invocationId(), args); + + final int recurrenceGoalId = hasura.createSchedulingSpecGoal( + "Recurrence Scheduling Test Goal", + recurrenceGoalDefinition, + specId, + 1).goalId(); + + final var resp = hasura.awaitScheduling(specId); + + final var plan = hasura.getPlan(planId); + final var activities = plan.activityDirectives(); + + assertEquals(52, activities.size()); + } } } diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java index 8a5eed0217..0aea2e3564 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java @@ -104,6 +104,14 @@ mutation CreateSchedulingSpecGoal($spec_goal: scheduling_specification_goals_ins specification_id } }"""), + CREATE_SCHEDULING_SPEC_GOAL_INVOCATION(""" + mutation CreateSchedulingSpecGoalInvocation($goal_id: Int!, $specification_id: Int!) { + insert_scheduling_specification_goals_one(object: {goal_id: $goal_id, specification_id: $specification_id}) { + goal_id + goal_invocation_id + priority + } + }"""), CREATE_USER(""" mutation createUser($user: users_insert_input!, $allowed_roles: [users_allowed_roles_insert_input!]!) { insert_users_one(object: $user) { diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java index 0e98a0ef7a..ccb55dbe50 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/HasuraRequests.java @@ -631,7 +631,7 @@ public SchedulingResponse awaitFailingScheduling(int schedulingSpecId, int timeo throw new RuntimeException(e); } } - case "complete", "failed" -> { + case "failed" -> { return response; } default -> fail("Scheduling returned bad status " + response.status() + " with reason " +response.reason()); @@ -735,6 +735,18 @@ public GoalInvocationId createSchedulingSpecProcedure( return GoalInvocationId.fromJSON(resp); } + public GoalInvocationId insertGoalInvocation(int goalId, int specificationId) throws IOException { + final var variables = Json.createObjectBuilder() + .add("goal_id", goalId) + .add("specification_id", specificationId) + .build(); + + final var resp = makeRequest(GQL.CREATE_SCHEDULING_SPEC_GOAL_INVOCATION, variables) + .getJsonObject("insert_scheduling_specification_goals_one"); + + return GoalInvocationId.fromJSON(resp); + } + public GoalInvocationId createSchedulingSpecGoal( String name, String definition, From 2e9e8349d1c6582163e4a629a7d5828ec2db4e1b Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Mon, 9 Sep 2024 12:14:37 -0700 Subject: [PATCH 100/108] clean up `SimulationDemo` --- .../procedures/SimulationDemo.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java index 5dd0f63af4..5ca804d72a 100644 --- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java +++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/procedures/SimulationDemo.java @@ -9,23 +9,12 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart; import org.jetbrains.annotations.NotNull; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; -import java.time.temporal.ChronoField; import java.util.Map; @SchedulingProcedure public record SimulationDemo(int quantity) implements Goal { @Override public void run(@NotNull final EditablePlan plan) { -// final var firstActivityTime = plan.toRelative(Instant.from(DOY_WITHOUT_ZONE_FORMATTER.parse("2024-128T07:00:00"))); -// -// plan.create( -// "BiteBanana", -// new DirectiveStart.Absolute(firstActivityTime), -// Map.of("biteSize", SerializedValue.of(2)) -// ); var simResults = plan.latestResults(); if (simResults == null) simResults = plan.simulate(); @@ -54,10 +43,4 @@ public void run(@NotNull final EditablePlan plan) { plan.commit(); } - - private static final DateTimeFormatter DOY_WITHOUT_ZONE_FORMATTER = new DateTimeFormatterBuilder() - .appendPattern("uuuu-DDD'T'HH:mm:ss") - .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true) - .toFormatter() - .withZone(ZoneOffset.UTC); } From d7df020a5dd7152e758302b9a98ec89c1c01fea8 Mon Sep 17 00:00:00 2001 From: "(skovati) Luke" Date: Mon, 9 Sep 2024 13:44:40 -0700 Subject: [PATCH 101/108] remove commented out exception throw --- .../procedural/processor/SchedulingProcedureProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java index d8dd19bf43..4fb25590e8 100644 --- a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java +++ b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java @@ -83,7 +83,7 @@ public boolean process(final Set annotations, final Round if (packageElement$.getKind() != ElementKind.PACKAGE) throw new RuntimeException("Only packages can be annotated with WithMappers"); packageElement = (PackageElement) packageElement$; } - if (packageElement == null) return false; //throw new RuntimeException("Need to annotate a package-info class with WithMappers"); + if (packageElement == null) return false; for (final var withMappersAnnotation : getRepeatableAnnotation(packageElement, WithMappers.class)) { final var attribute = getAnnotationAttribute(withMappersAnnotation, "value").orElseThrow(); From 5fe48b049c69f2b6961cdb4c26802e56486cc80a Mon Sep 17 00:00:00 2001 From: luke Date: Mon, 9 Sep 2024 13:47:23 -0700 Subject: [PATCH 102/108] remove whitespace Co-authored-by: Mythicaeda --- .../procedural/processor/SchedulingProcedureProcessor.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java index 4fb25590e8..1b0ce144fd 100644 --- a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java +++ b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java @@ -171,10 +171,8 @@ public boolean process(final Set annotations, final Round } } - // Allow other annotation processors to process the framework annotations. return false; - } @Override From fd3a649177a8be3393d1bf4aca5c553a9660a2bd Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Mon, 9 Sep 2024 15:10:37 -0700 Subject: [PATCH 103/108] Remove merlin framework dependency --- procedural/processor/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/procedural/processor/build.gradle b/procedural/processor/build.gradle index 2f24cb771e..ed12b0daab 100644 --- a/procedural/processor/build.gradle +++ b/procedural/processor/build.gradle @@ -11,7 +11,6 @@ java { dependencies { implementation project(':merlin-sdk') - implementation project(':merlin-framework') implementation project(':contrib') implementation project(':procedural:scheduling') implementation 'org.apache.commons:commons-lang3:3.13.0' From a3348f9d61db57fb39d88b4f02e40b4010ed99a2 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Mon, 9 Sep 2024 15:13:19 -0700 Subject: [PATCH 104/108] Add missing doc comments --- .../procedural/scheduling/simulation/SimulateOptions.kt | 1 + .../procedural/timeline/payloads/activities/Instance.kt | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/SimulateOptions.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/SimulateOptions.kt index 74361879c7..739ef6812b 100644 --- a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/SimulateOptions.kt +++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/simulation/SimulateOptions.kt @@ -5,5 +5,6 @@ package gov.nasa.ammos.aerie.procedural.scheduling.simulation // The following two options will be uncommented when checkpoint simulation is released. // val checkPointGeneration: CheckpointGeneration = CheckpointGeneration.None, // val checkpointRetention: CheckpointRetention = CheckpointRetention.All, + /** Configuration for when the simulation will pause. */ val pause: PauseBehavior = PauseBehavior.AtEnd, ) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt index f213dfe7d8..756584058b 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/activities/Instance.kt @@ -20,6 +20,12 @@ data class Instance( * Will be `null` if this is a child activity. */ @JvmField val directiveId: ActivityDirectiveId?, + + /** + * The maybe-null instance id of the instance that spawned this instance. + * + * Will be `null` if this is not a child activity. + */ @JvmField val parentId: ActivityInstanceId?, override val interval: Interval, ): Activity> { From 6bb972bd5459b05abc5c478737dfc2659392789d Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Mon, 9 Sep 2024 15:23:55 -0700 Subject: [PATCH 105/108] Publish procedural libraries --- procedural/constraints/build.gradle | 28 ++++++++++++++++++++++++++++ procedural/processor/build.gradle | 2 ++ procedural/remote/build.gradle | 28 ++++++++++++++++++++++++++++ procedural/scheduling/build.gradle | 5 +++++ procedural/timeline/build.gradle | 5 +++++ 5 files changed, 68 insertions(+) diff --git a/procedural/constraints/build.gradle b/procedural/constraints/build.gradle index afcf3a3069..94dd9aaa1a 100644 --- a/procedural/constraints/build.gradle +++ b/procedural/constraints/build.gradle @@ -5,6 +5,7 @@ plugins { id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.20' + id 'maven-publish' } repositories { @@ -35,6 +36,11 @@ kotlin { jvmToolchain(21) } +java { + withJavadocJar() + withSourcesJar() +} + var timelineSource = "${project(":procedural:timeline").projectDir}/src/main/kotlin" dokkaHtmlPartial.configure { @@ -55,3 +61,25 @@ dokkaHtmlPartial.configure { } } +publishing { + publications { + library(MavenPublication) { + version = findProperty('publishing.version') + from components.java + } + } + + publishing { + repositories { + maven { + name = findProperty("publishing.name") + url = findProperty("publishing.url") + credentials { + username = System.getenv(findProperty("publishing.usernameEnvironmentVariable")) + password = System.getenv(findProperty("publishing.passwordEnvironmentVariable")) + } + } + } + } +} + diff --git a/procedural/processor/build.gradle b/procedural/processor/build.gradle index ed12b0daab..b7c85efc1e 100644 --- a/procedural/processor/build.gradle +++ b/procedural/processor/build.gradle @@ -7,6 +7,8 @@ java { toolchain { languageVersion = JavaLanguageVersion.of(21) } + withJavadocJar() + withSourcesJar() } dependencies { diff --git a/procedural/remote/build.gradle b/procedural/remote/build.gradle index 6703ac0627..a4eecc4d3c 100644 --- a/procedural/remote/build.gradle +++ b/procedural/remote/build.gradle @@ -5,6 +5,7 @@ plugins { id "org.jetbrains.kotlin.jvm" version "2.0.20" id 'java-library' id 'org.jetbrains.dokka' version '1.9.20' + id 'maven-publish' } repositories { @@ -36,6 +37,11 @@ kotlin { jvmToolchain(21) } +java { + withJavadocJar() + withSourcesJar() +} + var timelineSource = "${project(":procedural:timeline").projectDir}/src/main/kotlin" var schedulingSource = "${project(":procedural:scheduling").projectDir}/src/main/kotlin" @@ -57,3 +63,25 @@ dokkaHtmlPartial.configure { } } } + +publishing { + publications { + library(MavenPublication) { + version = findProperty("publishing.version") + from components.java + } + } + + publishing { + repositories { + maven { + name = findProperty("publishing.name") + url = findProperty("publishing.url") + credentials { + username = System.getenv(findProperty("publishing.usernameEnvironmentVariable")) + password = System.getenv(findProperty("publishing.passwordEnvironmentVariable")) + } + } + } + } +} diff --git a/procedural/scheduling/build.gradle b/procedural/scheduling/build.gradle index 0d67c94c69..cf3b689f20 100644 --- a/procedural/scheduling/build.gradle +++ b/procedural/scheduling/build.gradle @@ -34,6 +34,11 @@ kotlin { jvmToolchain(21) } +java { + withJavadocJar() + withSourcesJar() +} + var timelineSource = "${project(":procedural:timeline").projectDir}/src/main/kotlin" dokkaHtmlPartial.configure { diff --git a/procedural/timeline/build.gradle b/procedural/timeline/build.gradle index 086afad4a4..1d9bc0f448 100644 --- a/procedural/timeline/build.gradle +++ b/procedural/timeline/build.gradle @@ -35,6 +35,11 @@ kotlin { jvmToolchain(21) } +java { + withJavadocJar() + withSourcesJar() +} + dokkaHtmlPartial.configure { dokkaSourceSets { configureEach { From d0dfd014efc43cbc0de28fe3872d58155d8b18b8 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Mon, 9 Sep 2024 15:25:00 -0700 Subject: [PATCH 106/108] Remove dokka directory of generated docs --- .github/workflows/deploy-to-gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-to-gh-pages.yml b/.github/workflows/deploy-to-gh-pages.yml index 932a69b1f8..1a1aa6753a 100644 --- a/.github/workflows/deploy-to-gh-pages.yml +++ b/.github/workflows/deploy-to-gh-pages.yml @@ -52,7 +52,7 @@ jobs: cp -a ./scheduler-driver/build/docs/javadoc ./build/javadoc/scheduler-driver cp -a ./scheduler-server/build/docs/javadoc ./build/javadoc/scheduler-server cp -a ./scheduler-worker/build/docs/javadoc ./build/javadoc/scheduler-worker - cp -a ./procedural/build/dokka/htmlMultiModule ./build/dokka/procedural-apis + cp -a ./procedural/build/dokka/htmlMultiModule ./build/procedural-apis - name: Upload Artifact uses: actions/upload-pages-artifact@v3 with: From afc72b0c566e1d0783d24e1c890058fcac2d2068 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Mon, 9 Sep 2024 15:41:37 -0700 Subject: [PATCH 107/108] Reimplement .isEqualTo, as deprecated --- .../nasa/jpl/aerie/merlin/protocol/types/Duration.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java b/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java index 712988c27c..e2ebc5e0de 100644 --- a/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java +++ b/merlin-sdk/src/main/java/gov/nasa/jpl/aerie/merlin/protocol/types/Duration.java @@ -650,4 +650,13 @@ public int compareTo(final Duration other) { return Long.compare(this.micros, other.micros); } + /** + * Delegates to `.equals(other)`. + * + * @deprecated use `.equals` instead. + */ + @Deprecated + public boolean isEqualTo(final Object other) { + return this.equals(other); + } } From a974ed3d78b7768fd5c652c2988102396f83494d Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Mon, 9 Sep 2024 15:41:49 -0700 Subject: [PATCH 108/108] Expand truncateList doc comment --- .../ammos/aerie/procedural/timeline/util/ListUtils.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListUtils.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListUtils.kt index 1bcb897faf..266f768647 100644 --- a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListUtils.kt +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/util/ListUtils.kt @@ -28,7 +28,16 @@ fun > preprocessList(list: List, shouldCoalesce: (V.(V) -> fun > listCollector(list: List, isSorted: Boolean = false, isSerial: Boolean = false) = { opts: CollectOptions -> truncateList(list, opts, isSorted, isSerial) } -/** Eagerly truncates a list of timeline objects to known bounds. */ +/** + * Eagerly truncates a list of timeline objects to known bounds. + * + * `exploitSorted` and `isSerial` exist to provide extra information about the list, for performance optimization. + * + * @param list The list of [IntervalLike] objects to be sorted. + * @param opts Collect options, specifies the truncation bounds and behavior. + * @param exploitSorted Whether the start times of the intervals are sorted. + * @param isSerial Whether the list is "serial", i.e. sorted by start time AND non-overlapping. + */ fun > truncateList(list: List, opts: CollectOptions, exploitSorted: Boolean, isSerial: Boolean): List = if (opts.bounds == Interval.MIN_MAX || list.isEmpty()) list else if (!exploitSorted) {