Skip to content

Commit

Permalink
Add bounds and time conversion to plan interface
Browse files Browse the repository at this point in the history
  • Loading branch information
JoelCourtney committed Feb 16, 2024
1 parent de7e9fe commit 54c92d7
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package gov.nasa.jpl.aerie.merlin.protocol.types;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;

Expand Down Expand Up @@ -372,15 +374,20 @@ public static Duration max(final Duration... durations) {
return maximum;
}

/** Apply a duration to a {@link java.time.Instant}. */
public static java.time.Instant addToInstant(final java.time.Instant instant, final Duration duration) {
/** Apply a duration to a {@link Instant}. */
public static java.time.Instant addToInstant(final Instant instant, final Duration duration) {
// Java Instants don't provide capability to add microseconds
// Add millis and micros separately to avoid possible overflow
return instant
.plusMillis(duration.dividedBy(Duration.MILLISECONDS))
.plusNanos(1000 * duration.remainderOf(Duration.MILLISECONDS).dividedBy(Duration.MICROSECONDS));
}

/** Find the duration between two {@link Instant}s. */
public static Duration durationBetweenInstants(final Instant a, final Instant b) {
return microseconds(a.until(b, ChronoUnit.MICROS));
}

public Duration negate() {
return Duration.negate(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package gov.nasa.jpl.aerie.timeline.remote
import gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration
import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue
import gov.nasa.jpl.aerie.timeline.Interval
import gov.nasa.jpl.aerie.timeline.Interval.Inclusivity.*
import gov.nasa.jpl.aerie.timeline.Segment
import gov.nasa.jpl.aerie.timeline.BaseTimeline
import gov.nasa.jpl.aerie.timeline.Interval.Companion.between
import gov.nasa.jpl.aerie.timeline.activities.AnyDirective
import gov.nasa.jpl.aerie.timeline.activities.AnyInstance
import gov.nasa.jpl.aerie.timeline.activities.Instance
Expand All @@ -16,29 +16,75 @@ import gov.nasa.jpl.aerie.timeline.collections.Instances
import gov.nasa.jpl.aerie.timeline.util.listCollector
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 AeriePostgresDatabaseFacade(
data class AeriePostgresPlan(
/** A connection to Aerie's database. */
val c: Connection,
/** The particular simulation dataset to query. */
val simDatasetId: Int
): DatabaseFacade {
): Plan {

private val datasetId by lazy {
val statement = c.prepareStatement(
"select dataset_id from simulation_dataset where id = ?;"
)
val statement = c.prepareStatement("select dataset_id from simulation_dataset where id = ?;")
statement.setInt(1, simDatasetId)
getSingleIntQueryResult(statement)
}

private val simulationId by lazy {
val statement = c.prepareStatement("select simulation_id from 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 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("Could not find dataset for simulation dataset $simDatasetId.")
val datasetId = result.getInt(1)
if (result.next()) throw DatabaseError("Multiple datasets found for simulation dataset $simDatasetId")
datasetId
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 plan where id = ?;")
statement.setInt(1, simulationInfo.planId)
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))
}
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): Duration = Duration.durationBetweenInstants(planInfo.startTime, abs)
override fun toAbsolute(rel: Duration): Instant = Duration.addToInstant(planInfo.startTime, rel)

private val intervalStyleStatement = c.prepareStatement("set intervalstyle = 'iso_8601';")
private val profileInfoStatement = c.prepareStatement(
"select id, duration from profile where dataset_id = ? and name = ?;"
Expand Down Expand Up @@ -73,7 +119,7 @@ data class AeriePostgresDatabaseFacade(
val thisStart = Duration.parseISO8601(response.getString(1))
if (previousStart !== null) {
val newSegment = Segment(
Interval.between(previousStart, thisStart, Inclusive, Exclusive),
between(previousStart, thisStart, Inclusive, Exclusive),
previousValue!!,
)
result.add(newSegment)
Expand All @@ -90,7 +136,7 @@ data class AeriePostgresDatabaseFacade(
if (previousStart !== null) {
result.add(
Segment(
Interval.between(previousStart, profileInfo.duration, Inclusive, Exclusive),
between(previousStart, profileInfo.duration, Inclusive, Exclusive),
previousValue!!
)
)
Expand Down Expand Up @@ -137,7 +183,7 @@ data class AeriePostgresDatabaseFacade(
AnyInstance(arguments, computedAttributes),
response.getString(4),
directiveId,
Interval.between(start, start.plus(Duration.parseISO8601(response.getString(2))))
between(start, start.plus(Duration.parseISO8601(response.getString(2))))
))
}
return BaseTimeline(::Instances, listCollector(result)).specialize()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
package gov.nasa.jpl.aerie.timeline.remote

import gov.nasa.jpl.aerie.merlin.protocol.types.Duration
import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue
import gov.nasa.jpl.aerie.timeline.Interval
import gov.nasa.jpl.aerie.timeline.Segment
import gov.nasa.jpl.aerie.timeline.activities.AnyDirective
import gov.nasa.jpl.aerie.timeline.activities.AnyInstance
import gov.nasa.jpl.aerie.timeline.Timeline
import gov.nasa.jpl.aerie.timeline.ops.coalesce.CoalesceSegments
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. */
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

/** An interface for querying timelines of profiles and activities. */
interface DatabaseFacade {
/**
* Query a resource profile from the database
*
Expand Down

0 comments on commit 54c92d7

Please sign in to comment.