diff --git a/procedural/constraints/src/test/java/gov/nasa/ammos/aerie/procedural/constraints/NotImplementedPlan.java b/procedural/constraints/src/test/java/gov/nasa/ammos/aerie/procedural/constraints/NotImplementedPlan.java index 18fb2ee459..3f22e74646 100644 --- a/procedural/constraints/src/test/java/gov/nasa/ammos/aerie/procedural/constraints/NotImplementedPlan.java +++ b/procedural/constraints/src/test/java/gov/nasa/ammos/aerie/procedural/constraints/NotImplementedPlan.java @@ -2,8 +2,10 @@ import gov.nasa.ammos.aerie.procedural.timeline.Interval; import gov.nasa.ammos.aerie.procedural.timeline.collections.Directives; +import gov.nasa.ammos.aerie.procedural.timeline.collections.ExternalEvents; import gov.nasa.ammos.aerie.procedural.timeline.ops.SerialSegmentOps; import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment; +import gov.nasa.ammos.aerie.procedural.timeline.plan.EventQuery; import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; @@ -51,4 +53,10 @@ public > TL resource( { throw new NotImplementedError(); } + + @NotNull + @Override + public ExternalEvents events(@NotNull final EventQuery query) { + throw new NotImplementedError(); + } } diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/ExternalEvents.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/ExternalEvents.kt new file mode 100644 index 0000000000..00ea957b43 --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/collections/ExternalEvents.kt @@ -0,0 +1,31 @@ +package gov.nasa.ammos.aerie.procedural.timeline.collections + +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.payloads.ExternalEvent +import gov.nasa.ammos.aerie.procedural.timeline.payloads.ExternalSource +import gov.nasa.ammos.aerie.procedural.timeline.util.preprocessList + +/** + * A timeline of external events. + */ +data class ExternalEvents(private val timeline: Timeline): + Timeline by timeline, + NonZeroDurationOps, + ParallelOps +{ + constructor(vararg events: ExternalEvent): this(events.asList()) + constructor(events: List): this(BaseTimeline(::ExternalEvents, preprocessList(events, null))) + + /** Filter by one or more types. */ + fun filterByType(vararg types: String) = filter { it.type in types } + + /** Filter by one or more event sources. */ + fun filterBySource(vararg sources: ExternalSource) = filter { it.source in sources } + + /** Filter by one or more derivation groups. */ + fun filterByDerivationGroup(vararg groups: String) = filter { it.source.derivationGroup in groups } +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/ExternalEvent.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/ExternalEvent.kt new file mode 100644 index 0000000000..110728d770 --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/ExternalEvent.kt @@ -0,0 +1,20 @@ +package gov.nasa.ammos.aerie.procedural.timeline.payloads + +import gov.nasa.ammos.aerie.procedural.timeline.Interval +import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue + +/** An external event instance. */ +data class ExternalEvent( + /** The string name of this event. */ + @JvmField + val key: String, + /** The type of the event. */ + @JvmField + val type: String, + /** The source this event comes from. */ + @JvmField + val source: ExternalSource, + override val interval: Interval, +): IntervalLike { + override fun withNewInterval(i: Interval) = ExternalEvent(key, type, source, i) +} diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/ExternalSource.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/ExternalSource.kt new file mode 100644 index 0000000000..a28d1d904d --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/payloads/ExternalSource.kt @@ -0,0 +1,14 @@ +package gov.nasa.ammos.aerie.procedural.timeline.payloads + +/** + * An external source instance. Used for querying purposes - see EventQuery.kt. + * The included fields represent the primary key used to identify External Sources. + */ +data class ExternalSource( + /** The string name of this source. */ + @JvmField + val key: String, + /** The derivation group that this source is a member of. */ + @JvmField + val derivationGroup: String, +) diff --git a/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/EventQuery.kt b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/EventQuery.kt new file mode 100644 index 0000000000..8e83478780 --- /dev/null +++ b/procedural/timeline/src/main/kotlin/gov/nasa/ammos/aerie/procedural/timeline/plan/EventQuery.kt @@ -0,0 +1,35 @@ +package gov.nasa.ammos.aerie.procedural.timeline.plan + +import gov.nasa.ammos.aerie.procedural.timeline.payloads.ExternalSource + +/** Fields for filtering events as they are queried. */ +data class EventQuery( + /** + * A nullable list of derivation groups; the event must belong to one of them if present. + * + * If null, all derivation groups are allowed. + */ + val derivationGroups: List?, + + /** + * A nullable list of eventTypes; the event must belong to one of them if present. + * + * If null, all types are allowed. + */ + val eventTypes: List?, + + /** + * A nullable list of sources (described as a tuple of the source's (key, derivation group name)); the event must + * belong to one of them if present. + * + * If null, all sources are allowed. + */ + val sources: List?, +) { + constructor(derivationGroup: String?, eventType: String?, source: ExternalSource?): this( + derivationGroup?.let { listOf(it) }, + eventType?.let { listOf(it) }, + source?.let { listOf(it) } + ) + constructor(): this(null as String?, null, null) +} 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 dfdf77287c..4a739b2f11 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 @@ -7,6 +7,7 @@ import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective import gov.nasa.ammos.aerie.procedural.timeline.collections.Directives import gov.nasa.ammos.aerie.procedural.timeline.ops.SerialSegmentOps import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment +import gov.nasa.ammos.aerie.procedural.timeline.collections.ExternalEvents import java.time.Instant /** An interface for querying plan information and simulation results. */ @@ -41,4 +42,9 @@ interface Plan { * @param name string name of the resource */ fun > resource(name: String, deserializer: (List>) -> TL): TL + + /** Get external events associated with this plan. */ + fun events(query: EventQuery): ExternalEvents + /** Get all external events across all derivation groups associated with this plan. */ + fun events() = events(EventQuery()) } 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 9495c91961..306aff1468 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 @@ -1,5 +1,6 @@ package gov.nasa.jpl.aerie.scheduler.goals; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.ExternalEvent; 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; @@ -43,7 +44,8 @@ public void run( final MissionModel missionModel, final Function lookupActivityType, final SimulationFacade simulationFacade, - final DirectiveIdGenerator idGenerator + final DirectiveIdGenerator idGenerator, + Map> eventsByDerivationGroup ) { final ProcedureMapper procedureMapper; try { @@ -57,6 +59,7 @@ public void run( final var planAdapter = new SchedulerToProcedurePlanAdapter( plan, planHorizon, + eventsByDerivationGroup, problem.getDiscreteExternalProfiles(), problem.getRealExternalProfiles() ); 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 a2ffc9e7f7..ae70e94449 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 @@ -1,5 +1,6 @@ package gov.nasa.jpl.aerie.scheduler.model; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.ExternalEvent; import gov.nasa.jpl.aerie.constraints.model.DiscreteProfile; import gov.nasa.jpl.aerie.constraints.model.LinearProfile; import gov.nasa.jpl.aerie.merlin.driver.MissionModel; @@ -47,6 +48,7 @@ public class Problem { private final Map realExternalProfiles = new HashMap<>(); private final Map discreteExternalProfiles = new HashMap<>(); + private Map> eventsByDerivationGroup = new HashMap<>(); /** * the initial seed plan to start scheduling from @@ -171,6 +173,10 @@ public void setExternalProfile(final Map realExternalProf this.discreteExternalProfiles.putAll(discreteExternalProfiles); } + public void setEventsByDerivationGroup(final Map> events) { + this.eventsByDerivationGroup = events; + } + public Map getRealExternalProfiles(){ return this.realExternalProfiles; } @@ -179,6 +185,8 @@ public Map getDiscreteExternalProfiles(){ return this.discreteExternalProfiles; } + public Map> getEventsByDerivationGroup() { return this.eventsByDerivationGroup; } + public void setGoals(List goals){ goalsOrderedByPriority.clear(); goalsOrderedByPriority.addAll(goals); 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 209a82f1e4..a6629d3182 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 @@ -46,7 +46,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.MICROSECOND; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.ZERO; -import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.min; /** * prototype scheduling algorithm that schedules activities for a plan @@ -323,7 +322,16 @@ private void satisfyGoal(Goal goal) throws SchedulingInterruptedException{ satisfyOptionGoal(optionGoal); } else if (goal instanceof Procedure procedure) { if (!analysisOnly) { - procedure.run(problem, plan.getEvaluation(), plan, problem.getMissionModel(), this.problem::getActivityType, this.simulationFacade, this.idGenerator); + procedure.run( + problem, + plan.getEvaluation(), + plan, + problem.getMissionModel(), + this.problem::getActivityType, + this.simulationFacade, + this.idGenerator, + this.problem.getEventsByDerivationGroup() + ); } } else { satisfyGoalGeneral(goal); 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 638ea9f979..c23b5cbdbc 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 @@ -2,11 +2,14 @@ package gov.nasa.jpl.aerie.scheduler.plan import gov.nasa.ammos.aerie.procedural.timeline.Interval import gov.nasa.ammos.aerie.procedural.timeline.collections.Directives +import gov.nasa.ammos.aerie.procedural.timeline.collections.ExternalEvents +import gov.nasa.ammos.aerie.procedural.timeline.payloads.ExternalEvent import gov.nasa.ammos.aerie.procedural.timeline.ops.SerialSegmentOps import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment 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.plan.EventQuery 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.constraints.model.DiscreteProfile @@ -23,8 +26,9 @@ import gov.nasa.jpl.aerie.scheduler.model.Plan as SchedulerPlan data class SchedulerToProcedurePlanAdapter( private val schedulerPlan: SchedulerPlan, private val planningHorizon: PlanningHorizon, + private val eventsByDerivationGroup: Map>, private val discreteExternalResources: Map, - private val realExternalResources: Map, + private val realExternalResources: Map ): TimelinePlan, SchedulerPlan by schedulerPlan { override fun totalBounds() = Interval.between(Duration.ZERO, planningHorizon.aerieHorizonDuration) @@ -55,6 +59,16 @@ data class SchedulerToProcedurePlanAdapter( return Directives(result) } + override fun events(query: EventQuery): ExternalEvents { + var result = if (query.derivationGroups != null) query.derivationGroups!!.flatMap { + eventsByDerivationGroup[it] + ?: throw Error("derivation group either doesn't exist or isn't associated with plan: $it") + } + else eventsByDerivationGroup.values.flatten() + if (query.eventTypes != null) result = result.filter { it.type in query.eventTypes!! } + if (query.sources != null) result = result.filter { it.source in query.sources!! } + return ExternalEvents(result) + } override fun > resource( name: String, deserializer: (List>) -> TL diff --git a/scheduler-server/build.gradle b/scheduler-server/build.gradle index 88cc2434bf..07287b3df4 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:timeline') implementation project(':procedural:scheduling') implementation project(':type-utils') 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 11ee8ef064..347b013262 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 @@ -1,5 +1,8 @@ package gov.nasa.jpl.aerie.scheduler.server.services; +import gov.nasa.ammos.aerie.procedural.timeline.Interval; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.ExternalEvent; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.ExternalSource; import gov.nasa.jpl.aerie.constraints.model.DiscreteProfile; import gov.nasa.jpl.aerie.constraints.model.LinearProfile; import gov.nasa.jpl.aerie.json.BasicParsers; @@ -60,7 +63,9 @@ import java.nio.file.Path; import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; @@ -979,8 +984,7 @@ public Optional> getExternalDatasets(final PlanId planId) @Override public ExternalProfiles getExternalProfiles(final PlanId planId) - throws MerlinServiceException, IOException - { + throws MerlinServiceException, IOException { final Map realProfiles = new HashMap<>(); final Map discreteProfiles = new HashMap<>(); final var resourceTypes = new ArrayList(); @@ -1004,7 +1008,48 @@ public ExternalProfiles getExternalProfiles(final PlanId planId) } } return new ExternalProfiles(realProfiles, discreteProfiles, resourceTypes); -} + } + + @Override + public Map> getExternalEvents(final PlanId planId, final Instant horizonStart) + throws MerlinServiceException, IOException { + final var derivationGroupsRequest = """ + query DerivationGroupsForPlan { + plan_derivation_group(where: {plan_id: {_eq: %d}}) { + derivation_group_name + } + } + """.formatted(planId.id()); + final JsonObject derivationGroupsResponse = postRequest(derivationGroupsRequest).get(); + final var derivationGroups = Json.createArrayBuilder( + derivationGroupsResponse.getJsonObject("data").getJsonArray("plan_derivation_group") + .stream().map($ -> $.asJsonObject().getString("derivation_group_name")).toList() + ).build(); + + final var eventsRequest = """ + query DerivedEventsForPlan { + derived_events(where: {derivation_group_name: {_in: %s}}) { + source_key + event_type_name + event_key + duration + derivation_group_name + source_range + start_time + valid_at + } + }""".formatted(derivationGroups); + final JsonObject eventsResponse = postRequest(eventsRequest).get(); + + final var data = eventsResponse.getJsonObject("data").getJsonArray("derived_events"); + final var unorganized = parseExternalEvents(data, horizonStart); + final var result = new HashMap>(); + for (final var event: unorganized) { + final var list = result.computeIfAbsent(event.source.derivationGroup, $ -> new ArrayList<>()); + list.add(event); + } + return result; + } private Collection extractResourceTypes(final ProfileSet profileSet){ final var resourceTypes = new ArrayList(); @@ -1085,7 +1130,7 @@ private ProfileSet parseProfiles(JsonArray dataset){ return new ProfileSet(realProfiles, discreteProfiles); } - public ResourceProfile> parseProfile(JsonObject profile, JsonParser dynamicsParser){ + private ResourceProfile> parseProfile(JsonObject profile, JsonParser dynamicsParser){ // Profile segments are stored with their start offset relative to simulation start // We must convert these to durations describing how long each segment lasts final var type = chooseP(discreteValueSchemaTypeP, realValueSchemaTypeP).parse(profile.getJsonObject("type")).getSuccessOrThrow(); @@ -1130,6 +1175,27 @@ public ResourceProfile> parseProfile(JsonObject pr return ResourceProfile.of(type, segments); } + private List parseExternalEvents(final JsonArray eventsJson, final Instant horizonStart) { + final var result = new ArrayList(); + for (final var eventJson : eventsJson) { + final var e = eventJson.asJsonObject(); + final var start = new Duration( + horizonStart.until(ZonedDateTime.parse(e.getString("start_time")).toInstant(), ChronoUnit.MICROS) + ); + final var end = start.plus(Duration.fromString(e.getString("duration"))); + result.add(new ExternalEvent( + e.getString("event_key"), + e.getString("event_type_name"), + new ExternalSource( + e.getString("source_key"), + e.getString("derivation_group_name") + ), + Interval.between(start, end) + )); + } + return result; + } + private Map parseSimulatedActivities(JsonArray simulatedActivitiesArray, Instant simulationStart) throws InvalidJsonException { diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/MerlinDatabaseService.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/MerlinDatabaseService.java index 8856c101fc..18939f8d1f 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/MerlinDatabaseService.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/MerlinDatabaseService.java @@ -1,5 +1,6 @@ package gov.nasa.jpl.aerie.scheduler.server.services; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.ExternalEvent; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; import gov.nasa.jpl.aerie.merlin.protocol.model.SchedulerModel; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; @@ -26,6 +27,7 @@ import java.io.IOException; import java.time.Instant; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -100,6 +102,9 @@ void ensurePlanExists(final PlanId planId) ExternalProfiles getExternalProfiles(final PlanId planId) throws MerlinServiceException, IOException; + Map> getExternalEvents(final PlanId planId, final Instant horizonStart) + throws MerlinServiceException, IOException; + /** * Gets resource types associated to a plan, those coming from the mission model as well as those coming from external dataset resources * @param planId the plan id diff --git a/scheduler-worker/build.gradle b/scheduler-worker/build.gradle index b139bcbc25..2e3d69b2b2 100644 --- a/scheduler-worker/build.gradle +++ b/scheduler-worker/build.gradle @@ -113,6 +113,8 @@ dependencies { implementation project(':scheduler-server') implementation project(':parsing-utilities') implementation project(':constraints') + implementation project(':procedural:timeline') + implementation project(':procedural:scheduling') implementation 'io.javalin:javalin:5.6.3' implementation 'org.slf4j:slf4j-simple:2.0.7' 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 5d046daeca..1347e2afac 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 @@ -9,6 +9,7 @@ import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -20,6 +21,7 @@ import java.util.jar.JarFile; import java.util.stream.Collectors; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.ExternalEvent; import gov.nasa.jpl.aerie.merlin.driver.MissionModel; import gov.nasa.jpl.aerie.merlin.driver.MissionModelLoader; import gov.nasa.jpl.aerie.merlin.driver.SimulationEngineConfiguration; @@ -147,12 +149,14 @@ public void schedule( schedulerMissionModel.schedulerModel() ); final var externalProfiles = loadExternalProfiles(planMetadata.planId()); + final var externalEventsByDerivationGroup = loadExternalEvents(planMetadata.planId(), planMetadata.horizon().getStartInstant()); final var initialSimulationResultsAndDatasetId = loadSimulationResults(planMetadata); //seed the problem with the initial plan contents final var loadedPlanComponents = loadInitialPlan(planMetadata, problem, initialSimulationResultsAndDatasetId.map(Pair::getKey)); problem.setInitialPlan(loadedPlanComponents.schedulerPlan(), initialSimulationResultsAndDatasetId.map(Pair::getKey)); problem.setExternalProfile(externalProfiles.realProfiles(), externalProfiles.discreteProfiles()); + problem.setEventsByDerivationGroup(externalEventsByDerivationGroup); //apply constraints/goals to the problem final var compiledGlobalSchedulingConditions = new ArrayList(); final var failedGlobalSchedulingConditions = new ArrayList>(); @@ -338,6 +342,12 @@ private ExternalProfiles loadExternalProfiles(final PlanId planId) return merlinDatabaseService.getExternalProfiles(planId); } + private Map> loadExternalEvents(final PlanId planId, final Instant horizonStart) + throws MerlinServiceException, IOException + { + return merlinDatabaseService.getExternalEvents(planId, horizonStart); + } + private Optional storeSimulationResults( SimulationData simulationData, PlanMetadata planMetadata, diff --git a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockMerlinDatabaseService.java b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockMerlinDatabaseService.java index 67ba1bbe02..f79c1acf1d 100644 --- a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockMerlinDatabaseService.java +++ b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/MockMerlinDatabaseService.java @@ -1,5 +1,6 @@ package gov.nasa.jpl.aerie.scheduler.worker.services; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.ExternalEvent; import gov.nasa.jpl.aerie.merlin.driver.SimulationResults; import gov.nasa.jpl.aerie.merlin.protocol.model.SchedulerModel; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; @@ -18,11 +19,13 @@ import gov.nasa.jpl.aerie.scheduler.server.models.PlanMetadata; import gov.nasa.jpl.aerie.scheduler.server.models.ResourceType; import gov.nasa.jpl.aerie.scheduler.server.services.MerlinDatabaseService; +import gov.nasa.jpl.aerie.scheduler.server.services.MerlinServiceException; import gov.nasa.jpl.aerie.types.ActivityDirective; import gov.nasa.jpl.aerie.types.ActivityDirectiveId; import gov.nasa.jpl.aerie.types.MissionModelId; import org.apache.commons.lang3.tuple.Pair; +import java.io.IOException; import java.nio.file.Path; import java.time.Instant; import java.util.ArrayList; @@ -157,6 +160,13 @@ public ExternalProfiles getExternalProfiles(final PlanId planId) { return externalProfiles; } + @Override + public Map> getExternalEvents(final PlanId planId, final Instant horizonStart) + throws MerlinServiceException, IOException + { + return Map.of(); + } + @Override public Collection getResourceTypes(final PlanId planId) { diff --git a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingDSLCompilationServiceTests.java b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingDSLCompilationServiceTests.java index d2e0c2fc26..efd274cb7a 100644 --- a/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingDSLCompilationServiceTests.java +++ b/scheduler-worker/src/test/java/gov/nasa/jpl/aerie/scheduler/worker/services/SchedulingDSLCompilationServiceTests.java @@ -1,5 +1,6 @@ package gov.nasa.jpl.aerie.scheduler.worker.services; +import gov.nasa.ammos.aerie.procedural.timeline.payloads.ExternalEvent; import gov.nasa.jpl.aerie.constraints.tree.ActivitySpan; import gov.nasa.jpl.aerie.constraints.tree.DiscreteProfileFromDuration; import gov.nasa.jpl.aerie.constraints.tree.DiscreteResource; @@ -49,6 +50,7 @@ import org.junit.jupiter.api.TestInstance; import java.io.IOException; +import java.time.Instant; import java.util.Collection; import java.util.List; import java.util.Map; @@ -109,6 +111,13 @@ public ExternalProfiles getExternalProfiles(final PlanId planId) { return null; } + @Override + public Map> getExternalEvents(final PlanId planId, final Instant horizonStart) + throws MerlinServiceException, IOException + { + return Map.of(); + } + @Override public Collection getResourceTypes(final PlanId planId) { return null;