From 7a8222bbbc4d1c41775fc3d1c0e322e01700426b Mon Sep 17 00:00:00 2001 From: maillard Date: Wed, 20 Sep 2023 16:09:48 -0700 Subject: [PATCH 1/5] Add justAfter and justBefore timing constraints in scheduling edsl --- scheduler-worker/build.gradle | 1 + .../src/libs/scheduler-edsl-fluent-api.ts | 33 ++++ .../services/SchedulingIntegrationTests.java | 145 +++++++++++++++++- 3 files changed, 177 insertions(+), 2 deletions(-) diff --git a/scheduler-worker/build.gradle b/scheduler-worker/build.gradle index 89b673f2d2..9c8068cad1 100644 --- a/scheduler-worker/build.gradle +++ b/scheduler-worker/build.gradle @@ -124,6 +124,7 @@ dependencies { testImplementation project(':examples:foo-missionmodel') testImplementation testFixtures(project(':scheduler-server')) testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.0' testImplementation 'org.assertj:assertj-core:3.24.2' testImplementation 'junit:junit:4.13.2' testImplementation 'javax.json.bind:javax.json.bind-api:1.0' diff --git a/scheduler-worker/scheduling-dsl-compiler/src/libs/scheduler-edsl-fluent-api.ts b/scheduler-worker/scheduling-dsl-compiler/src/libs/scheduler-edsl-fluent-api.ts index 8df710466c..0726a6fbca 100644 --- a/scheduler-worker/scheduling-dsl-compiler/src/libs/scheduler-edsl-fluent-api.ts +++ b/scheduler-worker/scheduling-dsl-compiler/src/libs/scheduler-edsl-fluent-api.ts @@ -592,6 +592,9 @@ export class ActivityExpression { } export class TimingConstraint { + + public static defaultPadding: Temporal.Duration = Temporal.Duration.from({microseconds:1}); + /** @internal **/ private constructor() {} /** @@ -633,6 +636,23 @@ export class TimingConstraint { singleton: false }) } + + /** + * Represents a precise time point at a defined offset (default: 1 microseconds, can be modified) from the start or end of a window. + * Equivalent to TimingConstraint.singleton(windowProperty).plus(TimingConstraint.defaultPadding) + * @param windowProperty either WindowProperty.START or WindowProperty.END + */ + public static justAfter(windowProperty: WindowProperty): SingletonTimingConstraint { + return this.singleton(windowProperty).plus(this.defaultPadding); + } + /** + * Represents a precise time point at a defined offset (default: -1 microseconds, can be modified) from the start or end of a window. + * Equivalent to TimingConstraint.singleton(windowProperty).minus(TimingConstraint.defaultPadding) + * @param windowProperty either WindowProperty.START or WindowProperty.END + */ + public static justBefore(windowProperty: WindowProperty): SingletonTimingConstraint { + return this.singleton(windowProperty).minus(this.defaultPadding); + } } /** @@ -837,6 +857,7 @@ declare global { matchingArgs: WindowsEDSL.Gen.ActivityTypeParameterMapWithUndefined[T]): ActivityExpression } class TimingConstraint { + public static defaultPadding: Temporal.Duration; /** * The singleton timing constraint represents a precise time point * at some offset from either the start or end of a window. @@ -862,6 +883,18 @@ declare global { * @param upperBound represents the (inclusive) upper bound of the time interval */ public static bounds(lowerBound: SingletonTimingConstraint, upperBound: SingletonTimingConstraint): FlexibleRangeTimingConstraint + /** + * Represents a precise time point at a defined offset (default: 1 microseconds, can be modified) from the start or end of a window. + * Equivalent to TimingConstraint.singleton(windowProperty).plus(TimingConstraint.defaultPadding) + * @param windowProperty either WindowProperty.START or WindowProperty.END + */ + public static justAfter(windowProperty: WindowProperty): SingletonTimingConstraint + /** + * Represents a precise time point at a defined offset (default: -1 microseconds, can be modified) from the start or end of a window. + * Equivalent to TimingConstraint.singleton(windowProperty).minus(TimingConstraint.defaultPadding) + * @param windowProperty either WindowProperty.START or WindowProperty.END + */ + public static justBefore(windowProperty: WindowProperty): SingletonTimingConstraint } var WindowProperty: typeof AST.WindowProperty var Operator: typeof AST.TimingConstraintOperator 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 825388ea1f..48187382ac 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 @@ -19,6 +19,8 @@ import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.HOUR; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.HOURS; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.MICROSECOND; +import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.MICROSECONDS; +import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.MILLISECONDS; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.MINUTE; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.MINUTES; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.SECOND; @@ -56,10 +58,12 @@ import gov.nasa.jpl.aerie.scheduler.server.services.ScheduleRequest; import gov.nasa.jpl.aerie.scheduler.server.services.ScheduleResults; import gov.nasa.jpl.aerie.scheduler.model.Plan; -import gov.nasa.jpl.aerie.scheduler.model.SchedulingActivityDirectiveId; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class SchedulingIntegrationTests { @@ -2744,7 +2748,144 @@ export default () => Goal.CoexistenceGoal({ assertEquals(SerializedValue.of("fromStem"), peelBanana.serializedActivity().getArguments().get("peelDirection")); } - /** + static Stream caseProviderJustAfter() { + return Stream.of( + Arguments.of("WindowProperty.START", Duration.of(10, MINUTES).plus(1, MILLISECONDS)), + Arguments.of("WindowProperty.END", Duration.of(70, MINUTES).plus(1, MILLISECONDS)) + ); + } + + @ParameterizedTest + @MethodSource("caseProviderJustAfter") + void testJustAfter(String timepoint, Duration resultingStartTime) { + /* + Start with a plan with B anchored to A + Goal: for each B, place a C + And make sure that C ends up in the right place + */ + final var activityDuration = Duration.of(1, Duration.HOUR); + final var tenMinutes = Duration.of(10, MINUTES); + final var results = runScheduler( + BANANANATION, + Map.of( + new ActivityDirectiveId(1L), + new ActivityDirective( + tenMinutes, + "GrowBanana", + Map.of( + "quantity", SerializedValue.of(1), + "growingDuration", SerializedValue.of(activityDuration.in(Duration.MICROSECONDS))), + null, + true)), + List.of(new SchedulingGoal(new GoalId(0L), """ + export default function(){ + TimingConstraint.defaultPadding = Temporal.Duration.from({milliseconds:1}) + return Goal.CoexistenceGoal({ + forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), + activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), + startsAt: TimingConstraint.justAfter(%s) + }) + } + """.formatted(timepoint), true)), + PLANNING_HORIZON); + + assertEquals(1, results.scheduleResults.goalResults().size()); + final var goalResult = results.scheduleResults.goalResults().get(new GoalId(0L)); + + assertTrue(goalResult.satisfied()); + assertEquals(1, goalResult.createdActivities().size()); + for (final var activity : goalResult.createdActivities()) { + assertNotNull(activity); + } + for (final var activity : goalResult.satisfyingActivities()) { + assertNotNull(activity); + } + + final var planByActivityType = partitionByActivityType(results.updatedPlan()); + final var peelBananas = planByActivityType.get("PeelBanana"); + final var growBananas = planByActivityType.get("GrowBanana"); + + assertEquals(1, peelBananas.size()); + assertEquals(1, growBananas.size()); + final var peelBanana = peelBananas.iterator().next(); + final var growBanana = growBananas.iterator().next(); + + + assertEquals(tenMinutes, growBanana.startOffset()); + assertEquals(SerializedValue.of(1), growBanana.serializedActivity().getArguments().get("quantity")); + + assertEquals(resultingStartTime, peelBanana.startOffset()); + assertEquals(SerializedValue.of("fromStem"), peelBanana.serializedActivity().getArguments().get("peelDirection")); + } + + static Stream caseProviderJustBefore() { + return Stream.of( + Arguments.of("WindowProperty.START", Duration.of(10, MINUTES).minus(1, MICROSECONDS)), + Arguments.of("WindowProperty.END", Duration.of(70, MINUTES).minus(1, MICROSECONDS)) + ); + } + + @ParameterizedTest + @MethodSource("caseProviderJustBefore") + void testJustBefore(String timepoint, Duration resultingStartTime) { + /* + Start with a plan with B anchored to A + Goal: for each B, place a C + And make sure that C ends up in the right place + */ + final var activityDuration = Duration.of(1, Duration.HOUR); + final var tenMinutes = Duration.of(10, MINUTES); + final var results = runScheduler( + BANANANATION, + Map.of( + new ActivityDirectiveId(1L), + new ActivityDirective( + tenMinutes, + "GrowBanana", + Map.of( + "quantity", SerializedValue.of(1), + "growingDuration", SerializedValue.of(activityDuration.in(Duration.MICROSECONDS))), + null, + true)), + List.of(new SchedulingGoal(new GoalId(0L), """ + export default () => Goal.CoexistenceGoal({ + forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana), + activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}), + startsAt: TimingConstraint.justBefore(%s) + }) + """.formatted(timepoint), true)), + PLANNING_HORIZON); + + assertEquals(1, results.scheduleResults.goalResults().size()); + final var goalResult = results.scheduleResults.goalResults().get(new GoalId(0L)); + + assertTrue(goalResult.satisfied()); + assertEquals(1, goalResult.createdActivities().size()); + for (final var activity : goalResult.createdActivities()) { + assertNotNull(activity); + } + for (final var activity : goalResult.satisfyingActivities()) { + assertNotNull(activity); + } + + final var planByActivityType = partitionByActivityType(results.updatedPlan()); + final var peelBananas = planByActivityType.get("PeelBanana"); + final var growBananas = planByActivityType.get("GrowBanana"); + + assertEquals(1, peelBananas.size()); + assertEquals(1, growBananas.size()); + final var peelBanana = peelBananas.iterator().next(); + final var growBanana = growBananas.iterator().next(); + + + assertEquals(tenMinutes, growBanana.startOffset()); + assertEquals(SerializedValue.of(1), growBanana.serializedActivity().getArguments().get("quantity")); + + assertEquals(resultingStartTime, peelBanana.startOffset()); + assertEquals(SerializedValue.of("fromStem"), peelBanana.serializedActivity().getArguments().get("peelDirection")); + } + + /** * Test that the scheduler can correctly place activities off of activities anchored to start after the start * of another activity. */ From f515a7c04c38e21b68819fa0dacb9b286a609cbc Mon Sep 17 00:00:00 2001 From: maillard Date: Thu, 21 Sep 2023 16:57:44 -0700 Subject: [PATCH 2/5] Closed closed intervals in scheduling --- .../scheduler/simulation/SimulationResultsConverter.java | 4 +--- .../java/gov/nasa/jpl/aerie/scheduler/TestApplyWhen.java | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) 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 d7f1bf34eb..0be3a6cead 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 @@ -47,9 +47,7 @@ public static gov.nasa.jpl.aerie.constraints.model.ActivityInstance convertToCon { final var startT = Duration.of(startTime.until(driverActivity.start(), ChronoUnit.MICROS), MICROSECONDS); final var endT = startT.plus(driverActivity.duration()); - final var activityInterval = startT.isEqualTo(endT) - ? Interval.between(startT, endT) - : Interval.betweenClosedOpen(startT, endT); + final var activityInterval = Interval.between(startT, endT); return new gov.nasa.jpl.aerie.constraints.model.ActivityInstance( id, driverActivity.type(), driverActivity.arguments(), activityInterval); diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestApplyWhen.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestApplyWhen.java index dada4b93e5..6b71101e41 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestApplyWhen.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestApplyWhen.java @@ -901,7 +901,8 @@ public void testCoexistenceWindows() { assertTrue(TestUtility.activityStartingAtTime(plan.get(), Duration.of(1, Duration.SECONDS), actTypeA)); assertTrue(TestUtility.activityStartingAtTime(plan.get(), Duration.of(14, Duration.SECONDS), actTypeA)); - assertEquals(3, problem.getSimulationFacade().countSimulationRestarts()); + assertTrue(TestUtility.activityStartingAtTime(plan.get(), Duration.of(12, Duration.SECONDS), actTypeA)); + assertEquals(4, problem.getSimulationFacade().countSimulationRestarts()); } @Test @@ -971,12 +972,13 @@ public void testCoexistenceWindowsCutoffMidActivity() { for(SchedulingActivityDirective a : plan.get().getActivitiesByTime()){ logger.debug(a.startOffset().toString() + ", " + a.duration().toString()); } - + assertEquals(10, plan.get().getActivitiesById().size()); assertTrue(TestUtility.activityStartingAtTime(plan.get(), Duration.of(2, Duration.SECONDS), actTypeB)); assertTrue(TestUtility.activityStartingAtTime(plan.get(), Duration.of(10, Duration.SECONDS), actTypeB)); assertTrue(TestUtility.activityStartingAtTime(plan.get(), Duration.of(16, Duration.SECONDS), actTypeB)); + assertTrue(TestUtility.activityStartingAtTime(plan.get(), Duration.of(23, Duration.SECONDS), actTypeB)); assertTrue(TestUtility.activityStartingAtTime(plan.get(), Duration.of(25, Duration.SECONDS), actTypeB)); - assertEquals(5, problem.getSimulationFacade().countSimulationRestarts()); + assertEquals(6, problem.getSimulationFacade().countSimulationRestarts()); } @Test From e0703ec561051c3795eb2a6dbce4894d3022f7f7 Mon Sep 17 00:00:00 2001 From: maillard Date: Mon, 2 Oct 2023 17:20:23 -0700 Subject: [PATCH 3/5] Replace singular imports by plural --- .../services/SchedulingIntegrationTests.java | 51 +++++++++---------- 1 file changed, 24 insertions(+), 27 deletions(-) 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 48187382ac..2e20d6f664 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 @@ -16,14 +16,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.HOUR; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.HOURS; -import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.MICROSECOND; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.MICROSECONDS; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.MILLISECONDS; -import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.MINUTE; import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.MINUTES; -import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.SECOND; +import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.SECONDS; import static org.junit.jupiter.api.Assertions.*; import gov.nasa.jpl.aerie.constraints.model.DiscreteProfile; @@ -414,7 +411,7 @@ void testCoexistencePartialAct() { "GrowBanana", Map.of( "quantity", SerializedValue.of(1), - "growingDuration", SerializedValue.of(Duration.MINUTE.in(MICROSECOND)) + "growingDuration", SerializedValue.of(Duration.MINUTE.in(MICROSECONDS)) ), null, true @@ -448,7 +445,7 @@ void testCoexistencePartialActWithParameter() { "GrowBanana", Map.of( "quantity", SerializedValue.of(1), - "growingDuration", SerializedValue.of(Duration.MINUTE.times(2).in(MICROSECOND)) + "growingDuration", SerializedValue.of(Duration.MINUTE.times(2).in(MICROSECONDS)) ), null, true @@ -476,7 +473,7 @@ void testCoexistencePartialActWithParameter() { "GrowBanana", Map.of( "quantity", SerializedValue.of(2), - "growingDuration", SerializedValue.of(Duration.MINUTE.times(2).in(MICROSECOND)) + "growingDuration", SerializedValue.of(Duration.MINUTE.times(2).in(MICROSECONDS)) ), null, true @@ -514,12 +511,12 @@ export default () => Goal.CoexistenceGoal({ final var growBananas = planByActivityType.get("GrowBanana"); assertEquals(3, growBananas.size()); final var planByTime = partitionByStartTime(results.updatedPlan()); - assertEquals(2, planByTime.get(MINUTE.times(10)).size()); + assertEquals(2, planByTime.get(MINUTES.times(10)).size()); var lookingFor = false; final var expectedCreation = new SerializedActivity("GrowBanana", Map.of("quantity", SerializedValue.of(1), - "growingDuration", SerializedValue.of(MINUTES.in(MICROSECOND)))); - for(final var actAtTime10: planByTime.get(MINUTE.times(10))){ + "growingDuration", SerializedValue.of(MINUTES.in(MICROSECONDS)))); + for(final var actAtTime10: planByTime.get(MINUTES.times(10))){ if(actAtTime10.serializedActivity().equals(expectedCreation)){ lookingFor = true; } @@ -530,11 +527,11 @@ export default () => Goal.CoexistenceGoal({ @Test void testRecurrenceWithActivityFinder() { final var expectedMatch1 = new ActivityDirective( - Duration.of(0, Duration.SECONDS), + Duration.of(0, SECONDS), "GrowBanana", Map.of( "quantity", SerializedValue.of(2), - "growingDuration", SerializedValue.of(Duration.of(1, Duration.SECONDS).in(Duration.MICROSECONDS))), + "growingDuration", SerializedValue.of(Duration.of(1, SECONDS).in(Duration.MICROSECONDS))), null, true); final var expectedMatch2 = new ActivityDirective( @@ -542,7 +539,7 @@ void testRecurrenceWithActivityFinder() { "GrowBanana", Map.of( "quantity", SerializedValue.of(2), - "growingDuration", SerializedValue.of(Duration.of(2, Duration.SECONDS).in(Duration.MICROSECONDS))), + "growingDuration", SerializedValue.of(Duration.of(2, SECONDS).in(Duration.MICROSECONDS))), null, true); @@ -556,7 +553,7 @@ void testRecurrenceWithActivityFinder() { "GrowBanana", Map.of( "quantity", SerializedValue.of(3), - "growingDuration", SerializedValue.of(Duration.of(3, Duration.SECONDS).in(Duration.MICROSECONDS))), + "growingDuration", SerializedValue.of(Duration.of(3, SECONDS).in(Duration.MICROSECONDS))), null, true) ), @@ -593,11 +590,11 @@ void testCardinalityGoalWithActivityFinder() { final var results = runScheduler( BANANANATION, List.of(new ActivityDirective( - Duration.of(0, Duration.SECONDS), + Duration.of(0, SECONDS), "GrowBanana", Map.of( "quantity", SerializedValue.of(2), - "growingDuration", SerializedValue.of(Duration.of(5, Duration.SECONDS).in(Duration.MICROSECONDS))), + "growingDuration", SerializedValue.of(Duration.of(5, SECONDS).in(Duration.MICROSECONDS))), null, true)), List.of(new SchedulingGoal(new GoalId(0L), @@ -689,7 +686,7 @@ void testSingleActivityPlanSimpleCoexistenceGoalWithWindowReference() { "GrowBanana", Map.of( "quantity", SerializedValue.of(1), - "growingDuration", SerializedValue.of(Duration.MINUTE.in(MICROSECOND)) + "growingDuration", SerializedValue.of(Duration.MINUTE.in(MICROSECONDS)) ), null, true @@ -726,7 +723,7 @@ export default () => Goal.CoexistenceGoal({ final var created = iterator.next(); assertEquals(SerializedValue.of(10), created.serializedActivity().getArguments().get("quantity")); - assertEquals(SerializedValue.of(Duration.of(2, Duration.MINUTES).in(MICROSECOND)), created.serializedActivity().getArguments().get("growingDuration")); + assertEquals(SerializedValue.of(Duration.of(2, Duration.MINUTES).in(MICROSECONDS)), created.serializedActivity().getArguments().get("growingDuration")); assertEquals(Duration.of(7, Duration.MINUTES), created.startOffset()); } @@ -1721,7 +1718,7 @@ void testExternalResource() { final var myBooleanResource = new DiscreteProfile( List.of( - new Segment<>(Interval.between(HOUR.times(2), HOUR.times(4)), SerializedValue.of(true)) + new Segment<>(Interval.between(HOURS.times(2), HOURS.times(4)), SerializedValue.of(true)) ) ).assignGaps(new DiscreteProfile(List.of(new Segment(Interval.FOREVER, SerializedValue.of(false))))); @@ -1749,18 +1746,18 @@ export default (): Goal => { assertEquals(1, results.updatedPlan().size()); final var planByActivityType = partitionByActivityType(results.updatedPlan()); final var peelBanana = planByActivityType.get("PeelBanana").iterator().next(); - assertEquals(HOUR.times(2), peelBanana.startOffset()); + assertEquals(HOURS.times(2), peelBanana.startOffset()); } @Test void testApplyWhen() { - final var growBananaDuration = Duration.of(1, Duration.SECONDS); + final var growBananaDuration = Duration.of(1, SECONDS); final var results = runScheduler( BANANANATION, List.of( new ActivityDirective( - Duration.of(1, Duration.SECONDS), + Duration.of(1, SECONDS), "GrowBanana", Map.of( "quantity", SerializedValue.of(1), @@ -1768,7 +1765,7 @@ void testApplyWhen() { null, true), new ActivityDirective( - Duration.of(2, Duration.SECONDS), + Duration.of(2, SECONDS), "GrowBanana", Map.of( "quantity", SerializedValue.of(1), @@ -1776,7 +1773,7 @@ void testApplyWhen() { null, true), new ActivityDirective( - Duration.of(3, Duration.SECONDS), + Duration.of(3, SECONDS), "GrowBanana", Map.of( "quantity", SerializedValue.of(1), @@ -1866,7 +1863,7 @@ void testGlobalSchedulingConditions_conditionSometimesTrue() { BANANANATION, List.of( new ActivityDirective( - Duration.of(24, HOURS).minus(MICROSECOND), + Duration.of(24, HOURS).minus(MICROSECONDS), "BiteBanana", Map.of("biteSize", SerializedValue.of(1)), null, @@ -3201,7 +3198,7 @@ export default () => Goal.CoexistenceGoal({ final var daemonChecker = daemonCheckers.iterator().next(); assertEquals(Duration.of(5, MINUTES), zeroDuration.startOffset()); - assertEquals(Duration.of(10, MINUTES).plus(Duration.of(1, SECOND)), daemonChecker.startOffset()); + assertEquals(Duration.of(10, MINUTES).plus(Duration.of(1, SECONDS)), daemonChecker.startOffset()); } /** @@ -3294,7 +3291,7 @@ export default () => Goal.CoexistenceGoal({ final var peels = planByActivityType.get("PeelBanana"); assertEquals(1, peels.size()); - assertEquals(peels.iterator().next().startOffset(), Duration.of(5, MINUTE).plus(Duration.of(2, activityDuration))); + assertEquals(peels.iterator().next().startOffset(), Duration.of(5, MINUTES).plus(Duration.of(2, activityDuration))); } @Test From 6141da0faeebd71d540556e08a77fc46b8dd3e61 Mon Sep 17 00:00:00 2001 From: maillard Date: Wed, 4 Oct 2023 09:55:19 -0700 Subject: [PATCH 4/5] Ensure that anchors are referencing existing activities in simfacade --- .../nasa/jpl/aerie/scheduler/simulation/SimulationFacade.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationFacade.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationFacade.java index e26658423f..cb996b6047 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationFacade.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/SimulationFacade.java @@ -329,6 +329,9 @@ private ActivityDirective schedulingActToActivityDir(SchedulingActivityDirective } } final var serializedActivity = new SerializedActivity(activity.getType().getName(), arguments); + if(activity.anchorId()!= null && !planActDirectiveIdToSimulationActivityDirectiveId.containsKey(activity.anchorId())){ + throw new RuntimeException("Activity with id "+ activity.anchorId() + " referenced as an anchor by activity " + activity.toString() + " is not present in the plan"); + } return new ActivityDirective( activity.startOffset(), serializedActivity, From e1465c7f0e31356344c59197f9a2351016ee509d Mon Sep 17 00:00:00 2001 From: maillard Date: Wed, 4 Oct 2023 10:00:36 -0700 Subject: [PATCH 5/5] Remove dependency to junit4 --- scheduler-worker/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/scheduler-worker/build.gradle b/scheduler-worker/build.gradle index 9c8068cad1..e8c0b63552 100644 --- a/scheduler-worker/build.gradle +++ b/scheduler-worker/build.gradle @@ -126,7 +126,6 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.0' testImplementation 'org.assertj:assertj-core:3.24.2' - testImplementation 'junit:junit:4.13.2' testImplementation 'javax.json.bind:javax.json.bind-api:1.0' testImplementation 'org.glassfish:javax.json:1.1.4' }