From 0f528614259702e4764b73a5d93a637f947bb218 Mon Sep 17 00:00:00 2001 From: maillard Date: Tue, 10 Oct 2023 10:54:10 -0700 Subject: [PATCH] More prints for scheduling --- .../jpl/aerie/constraints/time/Windows.java | 11 +++++ .../MissingActivityInstanceConflict.java | 2 +- .../MissingActivityTemplateConflict.java | 13 ++++++ .../conflicts/MissingAssociationConflict.java | 6 +++ .../activities/ActivityCreationTemplate.java | 36 ++++++++++++---- .../scheduler/goals/CardinalityGoal.java | 3 ++ .../scheduler/goals/CoexistenceGoal.java | 4 ++ .../aerie/scheduler/goals/RecurrenceGoal.java | 4 ++ .../simulation/ResumableSimulationDriver.java | 33 +++++++++++++- .../simulation/SimulationFacade.java | 6 +++ .../scheduler/solver/PrioritySolver.java | 43 +++++++++++++++---- .../server/remotes/postgres/GoalBuilder.java | 36 +++++++++++----- 12 files changed, 167 insertions(+), 30 deletions(-) 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 a4760f0eb3..73481adf4b 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 ActivityCreationTemplate getActTemplate() { return template; } + + @Override + public String toString(){ + return "Conflict: missing activity template [cardinality:" + + this.getCardinality() + + ", occurrences: " + + (this.getTotalDuration().isPresent() ? this.getTotalDuration() : "N/A") + +"] of type: " + + getActTemplate().getType().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/constraints/activities/ActivityCreationTemplate.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityCreationTemplate.java index 0c1f61adbd..7115b23298 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityCreationTemplate.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityCreationTemplate.java @@ -206,6 +206,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)); + } + } } /** * create activity if possible @@ -217,7 +224,9 @@ public Optional getLastEvent(){ public @NotNull Optional createActivity(String name, Windows windows, SimulationFacade facade, Plan plan, PlanningHorizon planningHorizon, 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 act = createInstanceForReal(name, window, facade, plan, planningHorizon, evaluationEnvironment); if (act.isPresent()) { return act; @@ -239,6 +248,7 @@ private Optional createInstanceForReal(final String if (this.endRange != null) { tnw.addEndInterval(name, this.endRange.start, this.endRange.end); } + Duration storedDur = null; if (this.duration != null) { final Optional duration; try { @@ -250,10 +260,15 @@ private Optional createInstanceForReal(final String throw new UnsupportedOperationException("Activity creation duration arguments cannot depend on simulation results.", e); } duration.ifPresent(d -> tnw.addDurationInterval(name, d, d)); + storedDur = duration.get(); } final var success = tnw.solveConstraints(); if (!success) { - logger.warn("Inconsistent temporal constraints, will try next opportunity for activity placement if it exists"); + logger.warn("Inconsistent static temporal constraints, will try next opportunity for activity placement if it exists"); + logger.info("Start range " + this.startRange); + logger.info("Start range " + this.endRange); + if(storedDur!=null) logger.info("dur range " + storedDur); + logger.info("interval range " + interval); return Optional.empty(); } final var solved = tnw.getAllData(name); @@ -440,12 +455,13 @@ private Optional rootFindingHelper( final TaskNetworkAdapter.TNActData solved, final SimulationFacade simulationFacade ) { + logger.info("Starting rootfinding trying to place activity "+ this.type.getName()); + final var maxIterations = 20; try { var endInterval = solved.end(); var startInterval = solved.start(); final var durationHalfEndInterval = endInterval.duration().dividedBy(2); - final var result = new EquationSolvingAlgorithms .SecantDurationAlgorithm() .findRoot( @@ -458,21 +474,23 @@ private Optional rootFindingHelper( durationHalfEndInterval, startInterval.start, startInterval.end, - 20); + maxIterations); // 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("Rootfinding exited because too many iterations ("+maxIterations+")"); } catch (EquationSolvingAlgorithms.NoSolutionException e) { - logger.debug("No solution"); + logger.info("Rootfinding: no solution"); } if(!history.events.isEmpty()) { try { @@ -481,6 +499,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/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 d166d0c2b8..d11eb09ffa 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 @@ -108,6 +108,9 @@ protected CardinalityGoal fill(CardinalityGoal goal) { if (occurrenceRange != null) { goal.occurrenceRange = occurrenceRange; } + if(name==null){ + goal.name = "CardinalityGoal_"+"thereExists_"+this.thereExists.getType().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 d3b276ec44..5309ee4437 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 @@ -159,6 +159,10 @@ protected CoexistenceGoal fill(CoexistenceGoal goal) { goal.alias = alias; + if(name==null){ + goal.name = "CoexistenceGoal_forEach_"+forEach.prettyPrint()+"_thereExists_"+this.thereExists.getType().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 7f15465378..88ebc0b16a 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.getType().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..4ebd545c4a 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.isEqualTo(Duration.ZERO) ? 0 : dur/curTime.in(Duration.SECONDS); + if(dur != 0) { + logger.warn("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"); + 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.warn("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 cb996b6047..546b3d4db1 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 @@ -157,6 +157,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"); if(insertedActivities.size() == 0) return Map.of(); computeSimulationResultsUntil(endTime); final Map childActivities = new HashMap<>(); @@ -183,6 +184,9 @@ public void removeAndInsertActivitiesFromSimulation( final Collection activitiesToRemove, final Collection activitiesToAdd) throws SimulationException { + logger.info("Removing("+activitiesToRemove.size()+")/Adding("+activitiesToAdd.size()+") activities from simulation"); + activitiesToRemove.stream().forEach(remove -> logger.info("Removing act starting at " + remove.startOffset())); + activitiesToAdd.stream().forEach(adding -> logger.info("Adding act starting at " + adding.startOffset())); var atLeastOneActualRemoval = false; for(final var act: activitiesToRemove){ if(insertedActivities.containsKey(act)){ @@ -204,10 +208,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 e775a5ae4b..f6118ee737 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 @@ -116,6 +116,7 @@ public Optional getNextSolution() { try { initializePlan(); if(problem.getInitialSimulationResults().isPresent()) { + logger.info("Loading initial simulation results from the DB"); simulationFacade.loadInitialSimResults(problem.getInitialSimulationResults().get()); } } catch (SimulationFacade.SimulationException e) { @@ -147,7 +148,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(); @@ -182,6 +183,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) { @@ -203,6 +205,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()){ @@ -562,10 +576,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){ @@ -584,7 +600,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"); } } } @@ -592,8 +611,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()); } @@ -612,6 +633,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.info("Need to compute simulation results until "+ this.problem.getPlanningHorizon().getEndAerie() + " (planning horizon end) for computing conflicts"); final var lastSimulationResults = this.getLatestSimResultsUpTo(this.problem.getPlanningHorizon().getEndAerie()); final var evaluationEnvironment = new EvaluationEnvironment(this.problem.getRealExternalProfiles(), this.problem.getDiscreteExternalProfiles()); final var rawConflicts = goal.getConflicts(plan, lastSimulationResults, evaluationEnvironment); @@ -668,7 +690,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 @@ -691,10 +712,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.info("Initial windows from conflict temporal context :" + possibleWindows.trueSegmentsToString()); possibleWindows = narrowByResourceConstraints(possibleWindows, resourceConstraints); - + logger.info("Windows after narrowing by resource constraints :" + possibleWindows.trueSegmentsToString()); possibleWindows = narrowGlobalConstraints(plan, missing, possibleWindows, this.problem.getGlobalConstraints(), missing.getEvaluationEnvironment()); - + logger.info("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 @@ -720,6 +742,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("Will try to instantiate activity in narrowed windows"); final var act = missingTemplate.getActTemplate().createActivity( goal.getName() + "_" + java.util.UUID.randomUUID(), startWindows, @@ -760,9 +783,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.info("Need to compute simulation results until "+ totalDomain.end + " for computing resource constraints"); final var latestSimulationResults = this.getLatestSimResultsUpTo(totalDomain.end); //iteratively narrow the windows from each constraint //REVIEW: could be some optimization in constraint ordering (smallest domain first to fail fast) @@ -776,7 +800,7 @@ private Windows narrowByResourceConstraints(Windows windows, break; } } - return ret; + return ret; } @@ -808,6 +832,7 @@ private Windows narrowGlobalConstraints( return tmp; } //make sure the simulation results cover the domain + logger.info("Need to compute simulation results until "+ tmp.maxTrueTimePoint().get().getKey() + " for computing global scheduling conditions"); final var latestSimulationResults = this.getLatestSimResultsUpTo(tmp.maxTrueTimePoint().get().getKey()); for (GlobalConstraint gc : constraints) { if (gc instanceof GlobalConstraintWithIntrospection c) { 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 8e36a0f015..b2abf474f0 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; @@ -26,6 +29,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; @@ -173,17 +177,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())