diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Windows.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Windows.java index a083641eab..6f5f99e15b 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Windows.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Windows.java @@ -674,4 +674,15 @@ public boolean equals(final Object other) { public String toString() { return segments.toString(); } + + /** + * toString only for the true segments + */ + public String trueSegmentsToString(){ + final var sb = new StringBuilder(); + sb.append("["); + this.iterateEqualTo(true).forEach(w -> sb.append(w.toString())); + sb.append("]"); + return sb.toString(); + } } diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingActivityInstanceConflict.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingActivityInstanceConflict.java index 5da496ccca..305bf211c2 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingActivityInstanceConflict.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingActivityInstanceConflict.java @@ -39,7 +39,7 @@ public MissingActivityInstanceConflict( @Override public String toString() { if (this.instance != null) { - return "Conflict : missing activity instance " + this.instance; + return "Conflict : missing activity instance " + this.instance + ". Produced by goal " + getGoal().getName(); } return "Empty conflict"; } diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingActivityTemplateConflict.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingActivityTemplateConflict.java index da1e17acbc..19592fd85d 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingActivityTemplateConflict.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingActivityTemplateConflict.java @@ -100,4 +100,17 @@ public ActivityTemplateGoal getGoal() { public ActivityExpression getActTemplate() { return template; } + + @Override + public String toString(){ + return "Conflict: missing activity template [cardinality:" + + this.getCardinality() + + ", duration: " + + (this.getTotalDuration().isPresent() ? this.getTotalDuration() : "N/A") + +"] of type: " + + getActTemplate().type().getName() + + " in temporal context " + + getTemporalContext().trueSegmentsToString() + + ". Produced by goal " + getGoal().getName(); + } } diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingAssociationConflict.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingAssociationConflict.java index f54de6add0..0c30697125 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingAssociationConflict.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/conflicts/MissingAssociationConflict.java @@ -29,4 +29,10 @@ public Collection getActivityInstancesToChooseFrom( public Windows getTemporalContext() { return null; } + + @Override + public String toString(){ + return "Conflict: missing association between goal and 1 activity of following set: " + + this.getActivityInstancesToChooseFrom() + ". Produced by goal " + getGoal().getName(); + } } diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/CardinalityGoal.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/CardinalityGoal.java index d6a311a791..cc50a40435 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/CardinalityGoal.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/CardinalityGoal.java @@ -107,6 +107,9 @@ protected CardinalityGoal fill(CardinalityGoal goal) { if (occurrenceRange != null) { goal.occurrenceRange = occurrenceRange; } + if(name==null){ + goal.name = "CardinalityGoal_"+"thereExists_"+this.thereExists.type().getName()+"_(duration:"+((this.durationRange != null) ? this.durationRange: "N/A") +", occurrences:" + ((this.occurrenceRange != null) ? this.occurrenceRange: "N/A") +")"; + } return goal; } }//Builder diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/CoexistenceGoal.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/CoexistenceGoal.java index f3e551fdd7..5740b9ed93 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/CoexistenceGoal.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/CoexistenceGoal.java @@ -157,6 +157,10 @@ protected CoexistenceGoal fill(CoexistenceGoal goal) { goal.alias = alias; + if(name==null){ + goal.name = "CoexistenceGoal_forEach_"+forEach.prettyPrint()+"_thereExists_"+this.thereExists.type().getName(); + } + return goal; } diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/RecurrenceGoal.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/RecurrenceGoal.java index 270efa6564..4e3c6c5749 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/RecurrenceGoal.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/RecurrenceGoal.java @@ -91,6 +91,10 @@ else if (every.max.isNegative()) { } goal.recurrenceInterval = every; + if(name==null){ + goal.name = "RecurrenceGoal_one_"+this.thereExists.type().getName()+"_every_["+goal.recurrenceInterval.min+","+goal.recurrenceInterval.max+"]"; + } + return goal; } diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/ResumableSimulationDriver.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/ResumableSimulationDriver.java index 09a7605368..3772e4851c 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/ResumableSimulationDriver.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/simulation/ResumableSimulationDriver.java @@ -30,6 +30,9 @@ import java.util.Optional; public class ResumableSimulationDriver implements AutoCloseable { + + public long durationSinceRestart = 0; + private static final Logger logger = LoggerFactory.getLogger(ResumableSimulationDriver.class); /* The current real time. All the tasks before and at this time have been performed. Simulation has not started so it is set to MIN_VALUE. */ @@ -66,14 +69,26 @@ public ResumableSimulationDriver(MissionModel missionModel, Duration plan initSimulation(); } + + private void printTimeSpent(){ + final var dur = durationSinceRestart/1_000_000_000.; + final var average = curTime.shorterThan(Duration.of(1, Duration.SECONDS)) ? 0 : dur/curTime.in(Duration.SECONDS); + if(dur != 0) { + logger.info("Time spent in the last sim " + dur + "s, average per simulation second " + average + "s. Simulated until " + curTime); + } + } + // This method is currently only used in one test. /*package-private*/ void clearActivitiesInserted() {activitiesInserted.clear();} /*package-private*/ void initSimulation(){ - logger.warn("Reinitialization of the scheduling simulation"); + logger.info("Reinitialization of the scheduling simulation"); + printTimeSpent(); + durationSinceRestart = 0; plannedDirectiveToTask.clear(); lastSimResults = null; lastSimResultsEnd = Duration.ZERO; + long before = System.nanoTime(); if (this.engine != null) this.engine.close(); this.engine = new SimulationEngine(); batch = null; @@ -96,6 +111,7 @@ public ResumableSimulationDriver(MissionModel missionModel, Duration plan batch = engine.extractNextJobs(Duration.MAX_VALUE); } } + this.durationSinceRestart += System.nanoTime() - before; countSimulationRestarts++; } @@ -109,10 +125,14 @@ public int getCountSimulationRestarts(){ @Override public void close() { + logger.debug("Closing sim"); + printTimeSpent(); this.engine.close(); } private void simulateUntil(Duration endTime){ + long before = System.nanoTime(); + logger.info("Simulating until "+endTime); assert(endTime.noShorterThan(curTime)); if(batch == null){ batch = engine.extractNextJobs(Duration.MAX_VALUE); @@ -129,7 +149,8 @@ private void simulateUntil(Duration endTime){ batch = engine.extractNextJobs(Duration.MAX_VALUE); } - lastSimResults = null; + lastSimResults = null; + this.durationSinceRestart += (System.nanoTime() - before); } @@ -165,6 +186,7 @@ public void simulateActivities(@NotNull Map schedule) { + final var before = System.nanoTime(); if (schedule.isEmpty()) { throw new IllegalArgumentException("simulateSchedule() called with empty schedule, use simulateUntil() instead"); } @@ -271,6 +299,7 @@ private void simulateSchedule(final Map } } lastSimResults = null; + this.durationSinceRestart+= System.nanoTime() - before; } /** 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 97aa3d22e9..da1fb2fea0 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 @@ -164,6 +164,7 @@ private ActivityDirectiveId getIdOfRootParent(SimulationResults results, Simulat public Map getAllChildActivities(final Duration endTime) throws SimulationException { + logger.info("Need to compute simulation results until "+ endTime + " for getting child activities"); var latestSimulationData = this.getLatestDriverSimulationResults(); //if no initial sim results and no sim has been performed, perform a sim and get the sim results if(latestSimulationData.isEmpty()){ @@ -196,6 +197,9 @@ public void removeAndInsertActivitiesFromSimulation( final Collection activitiesToRemove, final Collection activitiesToAdd) throws SimulationException { + logger.debug("Removing("+activitiesToRemove.size()+")/Adding("+activitiesToAdd.size()+") activities from simulation"); + activitiesToRemove.stream().forEach(remove -> logger.debug("Removing act starting at " + remove.startOffset())); + activitiesToAdd.stream().forEach(adding -> logger.debug("Adding act starting at " + adding.startOffset())); var atLeastOneActualRemoval = false; for(final var act: activitiesToRemove){ if(insertedActivities.containsKey(act)){ @@ -217,10 +221,12 @@ public void removeAndInsertActivitiesFromSimulation( allActivitiesToSimulate.addAll(insertedActivities.keySet()); insertedActivities.clear(); planActDirectiveIdToSimulationActivityDirectiveId.clear(); + logger.info("(Re)creating simulation driver because at least one removal("+atLeastOneActualRemoval+") or insertion in the past ("+earliestActStartTime+")"); if (driver != null) { this.pastSimulationRestarts += driver.getCountSimulationRestarts(); driver.close(); } + logger.info("Number of simulation restarts so far: " + this.pastSimulationRestarts); driver = new ResumableSimulationDriver<>(missionModel, planningHorizon.getAerieHorizonDuration()); } simulateActivities(allActivitiesToSimulate); 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 0dc35984ab..a33d75c673 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 @@ -103,6 +103,13 @@ public Optional getLastEvent(){ if(!events.isEmpty()) return Optional.of(events.get(events.size()-1)); return Optional.empty(); } + + public void logHistory(){ + logger.info("Rootfinding history"); + for(final var event: events){ + logger.info("Start:" + event.start + " end:" + (event.end==null ? "error" : event.end)); + } + } } /** @@ -141,6 +148,7 @@ public Optional getNextSolution() { try { initializePlan(); if(problem.getInitialSimulationResults().isPresent()) { + logger.debug("Loading initial simulation results from the DB"); simulationFacade.loadInitialSimResults(problem.getInitialSimulationResults().get()); } } catch (SimulationFacade.SimulationException e) { @@ -172,7 +180,7 @@ public record InsertActivityResult(boolean success, List acts){ // TODO: When anchors are allowed to be added by Scheduling goals, inserting the new activities one at a time should be reconsidered boolean allGood = true; - + logger.info("Inserting new activities in the plan to check plan validity"); for(var act: acts){ //if some parameters are left uninstantiated, this is the last moment to do it var duration = act.duration(); @@ -207,6 +215,7 @@ private InsertActivityResult checkAndInsertActs(Collection(); if(allGood) { + logger.info("New activities have been inserted in the plan successfully"); if(!acts.isEmpty()) simulationFacade.initialSimulationResultsAreStale(); //update plan with regard to simulation for(var act: acts) { @@ -221,6 +230,7 @@ private InsertActivityResult checkAndInsertActs(Collection 0 || durationLeft.longerThan(Duration.ZERO)){ + logger.info("Trying to satisfy template conflict " + i + " (iteration: "+(++nbIterations)+"). Missing cardinality: " + cardinalityLeft + ", duration: " + (durationLeft.isEqualTo(Duration.ZERO) ? "N/A" : durationLeft)); final var acts = getBestNewActivities(missingActivityTemplateConflict); assert acts != null; //add the activities to the output plan if (!acts.isEmpty()) { + logger.info("Found activity to satisfy missing activity template conflict"); final var insertionResult = checkAndInsertActs(acts); if(insertionResult.success()){ @@ -598,10 +626,12 @@ else if(!analysisOnly && (missing instanceof MissingActivityTemplateConflict mi .reduce(Duration.ZERO, Duration::plus)); } } else{ + logger.info("Conflict " + i + " could not be satisfied"); break; } } if(cardinalityLeft <= 0 && durationLeft.noLongerThan(Duration.ZERO)){ + logger.info("Conflict " + i + " has been addressed"); itConflicts.remove(); } } else if(missing instanceof MissingAssociationConflict missingAssociationConflict){ @@ -620,7 +650,10 @@ else if(!analysisOnly && (missing instanceof MissingActivityTemplateConflict mi //decision-making here, we choose the first satisfying activity evaluation.forGoal(goal).associate(act, false); itConflicts.remove(); + logger.info("Activity " + act + " has been associated to goal " + goal.getName() +" to satisfy conflict " + i); break; + } else{ + logger.info("Activity " + act + " could not be associated to goal " + goal.getName() + " because of goal constraints"); } } } @@ -628,8 +661,10 @@ else if(!analysisOnly && (missing instanceof MissingActivityTemplateConflict mi if(!missingConflicts.isEmpty() && goal.shouldRollbackIfUnsatisfied()){ + logger.warn("Rolling back changes for "+goal.getName()); rollback(goal); } + logger.info("Finishing goal satisfaction for goal " + goal.getName() +":"+ (missingConflicts.size() == 0 ? "SUCCESS" : "FAILURE. Number of conflicts that could not be addressed: " + missingConflicts.size())); evaluation.forGoal(goal).setScore(-missingConflicts.size()); } @@ -648,6 +683,7 @@ else if(!analysisOnly && (missing instanceof MissingActivityTemplateConflict mi assert goal != null; assert plan != null; //REVIEW: maybe should have way to request only certain kinds of conflicts + logger.debug("Computing simulation results until "+ this.problem.getPlanningHorizon().getEndAerie() + " (planning horizon end) in order to compute conflicts"); final var lastSimulationResults = this.getLatestSimResultsUpTo(this.problem.getPlanningHorizon().getEndAerie()); synchronizeSimulationWithSchedulerPlan(); final var evaluationEnvironment = new EvaluationEnvironment(this.problem.getRealExternalProfiles(), this.problem.getDiscreteExternalProfiles()); @@ -705,7 +741,6 @@ else if(!analysisOnly && (missing instanceof MissingActivityTemplateConflict mi //start from the time interval where the missing activity causes a problem //NB: these are start windows var possibleWindows = missing.getTemporalContext(); - //prune based on constraints on goal and activity type (mutex, state, //event, etc) //TODO: move this into polymorphic method. don't want to be demuxing types @@ -728,10 +763,11 @@ else if(!analysisOnly && (missing instanceof MissingActivityTemplateConflict mi //TODO: placeholder for now to avoid mutex fall through throw new IllegalArgumentException("request to create activities for conflict of unrecognized type"); } + logger.debug("Initial windows from conflict temporal context :" + possibleWindows.trueSegmentsToString()); possibleWindows = narrowByResourceConstraints(possibleWindows, resourceConstraints); - + logger.debug("Windows after narrowing by resource constraints :" + possibleWindows.trueSegmentsToString()); possibleWindows = narrowGlobalConstraints(plan, missing, possibleWindows, this.problem.getGlobalConstraints(), missing.getEvaluationEnvironment()); - + logger.debug("Windows after narrowing by global scheduling conditions :" + possibleWindows.trueSegmentsToString()); //narrow to windows where activity duration will fit var startWindows = possibleWindows; //for now handling just start-time windows, so no need to prune duration @@ -757,6 +793,7 @@ else if(!analysisOnly && (missing instanceof MissingActivityTemplateConflict mi startWindows = startWindows.and(missing.getTemporalContext()); //create the new activity instance (but don't place in schedule) //REVIEW: not yet handling multiple activities at a time + logger.info("Instantiating activity in windows " + startWindows.trueSegmentsToString()); final var act = createOneActivity( missingTemplate.getActTemplate(), goal.getName() + "_" + java.util.UUID.randomUUID(), @@ -795,9 +832,10 @@ private Windows narrowByResourceConstraints(Windows windows, if (windows.stream().noneMatch(Segment::value) || constraints.isEmpty()) { return ret; } - + logger.info("Narrowing windows by resource constraints"); final var totalDomain = Interval.between(windows.minTrueTimePoint().get().getKey(), windows.maxTrueTimePoint().get().getKey()); //make sure the simulation results cover the domain + logger.debug("Computing simulation results until "+ totalDomain.end + " in order to compute resource constraints"); final var latestSimulationResults = this.getLatestSimResultsUpTo(totalDomain.end); synchronizeSimulationWithSchedulerPlan(); //iteratively narrow the windows from each constraint @@ -812,7 +850,7 @@ private Windows narrowByResourceConstraints(Windows windows, break; } } - return ret; + return ret; } @@ -841,6 +879,7 @@ private Windows narrowGlobalConstraints( return tmp; } //make sure the simulation results cover the domain + logger.debug("Computing simulation results until "+ tmp.maxTrueTimePoint().get().getKey() + " in order to compute global scheduling conditions"); final var latestSimulationResults = this.getLatestSimResultsUpTo(tmp.maxTrueTimePoint().get().getKey()); synchronizeSimulationWithSchedulerPlan(); for (GlobalConstraint gc : constraints) { @@ -872,7 +911,9 @@ private Windows narrowGlobalConstraints( final Windows windows, final EvaluationEnvironment evaluationEnvironment) { //REVIEW: how to properly export any flexibility to instance? + logger.info("Trying to create one activity, will loop through possible windows"); for (var window : windows.iterateEqualTo(true)) { + logger.info("Trying in window " + window); var activity = instantiateActivity(activityExpression, name, window, evaluationEnvironment); if (activity.isPresent()) { return activity; @@ -898,9 +939,9 @@ private Optional instantiateActivity( if (activityExpression.endRange() != null) { taskNetwork.addEndInterval(name, activityExpression.endRange().start, activityExpression.endRange().end); } + Optional durRequirementLower = Optional.empty(); + Optional durRequirementUpper = Optional.empty();; if (activityExpression.durationRange() != null) { - final Optional durRequirementLower; - final Optional durRequirementUpper; try { durRequirementLower = activityExpression.durationRange().getLeft() .evaluate(null, planningHorizon.getHor(), evaluationEnvironment) @@ -920,7 +961,15 @@ private Optional instantiateActivity( } final var success = taskNetwork.solveConstraints(); if (!success) { - logger.warn("Inconsistent temporal constraints, will try next opportunity for activity placement if it exists"); + logger.debug("Inconsistent static temporal constraints, cannot place activity in interval"); + logger.debug("Start range " + activityExpression.startRange()); + logger.debug("End range " + activityExpression.endRange()); + logger.debug( + "Duration range [" + + (durRequirementLower.isPresent() ? durRequirementLower.get() : "-inf") + + ", " + + (durRequirementUpper.isPresent() ? durRequirementUpper.get() : "+inf")); + logger.debug("Interval range " + interval); return Optional.empty(); } final var solved = taskNetwork.getAllData(name); @@ -1096,17 +1145,19 @@ private Optional rootFindingHelper( // TODO: When scheduling is allowed to create activities with anchors, this constructor should pull from an expanded creation template final var lastActivityTested = result.history().getLastEvent(); + logger.info("Finished rootfinding: SUCCESS"); + history.logHistory(); return Optional.of(lastActivityTested.get().activity); } catch (EquationSolvingAlgorithms.ZeroDerivativeException zeroOrInfiniteDerivativeException) { - logger.debug("Rootfinding encountered a zero-derivative"); + logger.info("Rootfinding encountered a zero-derivative"); } catch (EquationSolvingAlgorithms.InfiniteDerivativeException infiniteDerivativeException) { - logger.debug("Rootfinding encountered an infinite-derivative"); + logger.info("Rootfinding encountered an infinite-derivative"); } catch (EquationSolvingAlgorithms.DivergenceException e) { - logger.debug("Rootfinding diverged"); + logger.info("Rootfinding diverged"); } catch (EquationSolvingAlgorithms.ExceededMaxIterationException e) { - logger.debug("Too many iterations"); + logger.info("Too many iterations"); } catch (EquationSolvingAlgorithms.NoSolutionException e) { - logger.debug("No solution"); + logger.info("Rootfinding found no solution"); } if(!history.events.isEmpty()) { try { @@ -1115,6 +1166,8 @@ private Optional rootFindingHelper( throw new RuntimeException("Exception while simulating original plan after activity insertion failure" ,e); } } + logger.info("Finished rootfinding: FAILURE"); + history.logHistory(); return Optional.empty(); } diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestUnsatisfiableCompositeGoals.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestUnsatisfiableCompositeGoals.java index 2298e70fba..9340efa3e3 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestUnsatisfiableCompositeGoals.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestUnsatisfiableCompositeGoals.java @@ -137,7 +137,7 @@ public void testAndWithBackTrack(){ Assertions.assertTrue(TestUtility.activityStartingAtTime(plan, t1hr, actTypeControllable)); Assertions.assertTrue(TestUtility.activityStartingAtTime(plan, t2hr, actTypeControllable)); Assertions.assertEquals(plan.getActivities().size(), 2); - assertEquals(4, problem.getSimulationFacade().countSimulationRestarts()); + assertEquals(2, problem.getSimulationFacade().countSimulationRestarts()); } @Test 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 3a152aeec3..55f22de814 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 @@ -1,5 +1,8 @@ package gov.nasa.jpl.aerie.scheduler.server.remotes.postgres; +import gov.nasa.jpl.aerie.constraints.model.ActivityInstance; +import gov.nasa.jpl.aerie.constraints.model.EvaluationEnvironment; +import gov.nasa.jpl.aerie.constraints.model.SimulationResults; import gov.nasa.jpl.aerie.constraints.time.Interval; import gov.nasa.jpl.aerie.constraints.time.Spans; import gov.nasa.jpl.aerie.constraints.time.Windows; @@ -25,6 +28,7 @@ import gov.nasa.jpl.aerie.scheduler.server.models.SchedulingDSL; import gov.nasa.jpl.aerie.scheduler.server.models.Timestamp; import gov.nasa.jpl.aerie.scheduler.server.services.UnexpectedSubtypeError; +import org.apache.commons.lang3.function.TriFunction; import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -172,17 +176,29 @@ private static Expression spansOfConstraintExpression( final SchedulingDSL.ConstraintExpression constraintExpression) { if (constraintExpression instanceof SchedulingDSL.ConstraintExpression.ActivityExpression c) { return new ForEachActivitySpans( - ($, simResults, environment) -> { - final var startTime = $.interval.start; - if (!$.type.equals(c.type())) return false; - for (final var arg: c - .arguments() - .map(expr -> expr.evaluateMap(simResults, startTime, environment)) - .orElse(Map.of()) - .entrySet()) { - if (!arg.getValue().equals($.parameters.get(arg.getKey()))) return false; + new TriFunction<>() { + @Override + public Boolean apply( + final ActivityInstance activityInstance, + final SimulationResults simResults, + final EvaluationEnvironment environment) + { + final var startTime = activityInstance.interval.start; + if (!activityInstance.type.equals(c.type())) return false; + for (final var arg : c + .arguments() + .map(expr -> expr.evaluateMap(simResults, startTime, environment)) + .orElse(Map.of()) + .entrySet()) { + if (!arg.getValue().equals(activityInstance.parameters.get(arg.getKey()))) return false; + } + return true; + } + + @Override + public String toString() { + return "(filter by ActivityExpression)"; } - return true; }, "alias" + c.type(), new ActivitySpan("alias" + c.type())