From 730f3495511ed7fa72203e9c72bc8a84d7b037cf Mon Sep 17 00:00:00 2001 From: maillard Date: Mon, 25 Sep 2023 13:37:55 -0700 Subject: [PATCH] Refactor of ActivityExpression and derived classes --- .../scheduler/EquationSolvingAlgorithms.java | 6 - .../MissingActivityTemplateConflict.java | 8 +- .../activities/ActivityCreationTemplate.java | 501 ------------------ .../ActivityCreationTemplateDisjunction.java | 159 ------ .../activities/ActivityExpression.java | 318 ++++------- .../ActivityExpressionDisjunction.java | 78 --- .../scheduler/goals/ActivityTemplateGoal.java | 27 +- .../scheduler/goals/CardinalityGoal.java | 1 - .../scheduler/goals/CoexistenceGoal.java | 23 +- .../aerie/scheduler/goals/RecurrenceGoal.java | 2 +- .../scheduler/model/SchedulingCondition.java | 2 +- .../scheduler/solver/PrioritySolver.java | 289 +++++++++- .../aerie/scheduler/FixedDurationTest.java | 6 +- .../scheduler/ParametricDurationTest.java | 7 +- .../aerie/scheduler/PrioritySolverTest.java | 17 +- .../aerie/scheduler/SimulationFacadeTest.java | 5 +- .../jpl/aerie/scheduler/TestApplyWhen.java | 123 +++-- .../aerie/scheduler/TestCardinalityGoal.java | 6 +- .../aerie/scheduler/TestRecurrenceGoal.java | 10 +- .../scheduler/TestRecurrenceGoalExtended.java | 34 +- .../TestUnsatisfiableCompositeGoals.java | 7 +- .../scheduler/UncontrollableDurationTest.java | 13 +- .../server/remotes/postgres/GoalBuilder.java | 7 +- 23 files changed, 508 insertions(+), 1141 deletions(-) delete mode 100644 scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityCreationTemplate.java delete mode 100644 scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityCreationTemplateDisjunction.java delete mode 100644 scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityExpressionDisjunction.java diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/EquationSolvingAlgorithms.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/EquationSolvingAlgorithms.java index 2cd3f868d2..ea24a04833 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/EquationSolvingAlgorithms.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/EquationSolvingAlgorithms.java @@ -32,7 +32,6 @@ RootFindingResult findRoot(Function f, public interface Function { T valueAt(T x, History historyType); - boolean isApproximation(); } public static class ZeroDerivativeException extends Exception{ @@ -89,11 +88,6 @@ public RootFindingResult findRoot( public Duration valueAt(final Duration x, final History history) { return f.valueAt(x, history).minus(y); } - - @Override - public boolean isApproximation() { - return f.isApproximation(); - } }; double x_nminus1_double = x0.in(Duration.MICROSECONDS); 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..da1e17acbc 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 @@ -3,7 +3,7 @@ import gov.nasa.jpl.aerie.constraints.model.EvaluationEnvironment; import gov.nasa.jpl.aerie.constraints.time.Windows; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; +import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.goals.ActivityTemplateGoal; import java.util.Optional; @@ -28,7 +28,7 @@ public class MissingActivityTemplateConflict extends MissingActivityConflict { public MissingActivityTemplateConflict( ActivityTemplateGoal goal, Windows temporalContext, - ActivityCreationTemplate template, + ActivityExpression template, EvaluationEnvironment evaluationEnvironment, int cardinality, Optional totalDuration) @@ -95,9 +95,9 @@ public ActivityTemplateGoal getGoal() { /** * The conflict can constraint the goal template to guide the search */ - private final ActivityCreationTemplate template; + private final ActivityExpression template; - public ActivityCreationTemplate getActTemplate() { + public ActivityExpression getActTemplate() { return template; } } 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 deleted file mode 100644 index 0c1f61adbd..0000000000 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityCreationTemplate.java +++ /dev/null @@ -1,501 +0,0 @@ -package gov.nasa.jpl.aerie.scheduler.constraints.activities; - -import gov.nasa.jpl.aerie.constraints.model.EvaluationEnvironment; -import gov.nasa.jpl.aerie.constraints.model.Profile; -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; -import gov.nasa.jpl.aerie.constraints.tree.DiscreteProfileFromDuration; -import gov.nasa.jpl.aerie.constraints.tree.DurationLiteral; -import gov.nasa.jpl.aerie.constraints.tree.Expression; -import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; -import gov.nasa.jpl.aerie.merlin.protocol.types.DurationType; -import gov.nasa.jpl.aerie.merlin.protocol.types.InstantiationException; -import gov.nasa.jpl.aerie.scheduler.EquationSolvingAlgorithms; -import gov.nasa.jpl.aerie.scheduler.NotNull; -import gov.nasa.jpl.aerie.scheduler.model.SchedulingActivityDirective; -import gov.nasa.jpl.aerie.scheduler.model.ActivityType; -import gov.nasa.jpl.aerie.scheduler.model.Plan; -import gov.nasa.jpl.aerie.scheduler.model.PlanningHorizon; -import gov.nasa.jpl.aerie.scheduler.simulation.SimulationFacade; -import gov.nasa.jpl.aerie.scheduler.solver.stn.TaskNetwork; -import gov.nasa.jpl.aerie.scheduler.solver.stn.TaskNetworkAdapter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -/** - * criteria used to identify create activity instances in scheduling goals - * - * the creation template is a partial specification of what is required of - * an activity instance in order to meet the goal criteria, but also includes - * additional information on how to create such matching instances in the - * event that no matches were found - * - * for example "an image of at least 10s (or 30s by default) duration taken - * with green filter" - * - * corresponds roughly to the concept of an "activity preset" - * - * creation templates may be fluently constructed via builders that parse like - * first order logic predicate clauses used in building up scheduling rules - */ -public class ActivityCreationTemplate extends ActivityExpression implements Expression { - - private static final Logger logger = LoggerFactory.getLogger(ActivityCreationTemplate.class); - - - /** - * ctor is private to prevent inconsistent construction - * - * please use the enclosed fluent Builder class instead - * - * leaves all criteria elements unspecified - */ - protected ActivityCreationTemplate() { } - - @Override - @SuppressWarnings("unchecked") - public , AT extends ActivityExpression> AbstractBuilder getNewBuilder() { - return (AbstractBuilder) new Builder(); - } - - public static ActivityCreationTemplate ofType(ActivityType actType) { - var act = new ActivityCreationTemplate(); - act.type = actType; - return act; - } - - - /** - * fluent builder class for constructing creation templates - * - * each different term added to the builder via method calls become part of - * a logical conjection, ie matching activities must meet all of the - * specified criteria - * - * existing terms can be replaced by calling the same method again, ie - * matching activities must only meet the last-specified term - * - * if the scheduling algorithm needs to create a new activity instance, it - * will use either the last-specified default value for the template or, if - * this template doesn't specify a value, the activity type's own default - * value - * - * creation templates must always specify an activity type (and for now also - * a duration) - * //REVIEW: eventually duration should come from simulation instead - */ - public static class Builder extends AbstractBuilder { - - //REVIEW: perhaps separate search criteria vs default specification, - // eg Range(0...5) allowed, but create with 4 - - /** - * create activity instances with given default duration - * - * @param duration IN STORED the duration of the activity created by - * this template. not null - * @return the same builder object updated with new criteria - */ - public @NotNull - Builder duration(@NotNull Duration duration) { - this.durationIn = new DiscreteProfileFromDuration(new DurationLiteral(duration)); - return getThis(); - } - - public @NotNull - Builder duration(@NotNull Expression> duration) { - this.durationIn = duration; - return getThis(); - } - - // parametric dur - //-> either state value - // or - //recognize the dur in the parameters instead of a specific builder ? - - - @Override - public Builder basedOn(ActivityCreationTemplate template) { - type = template.type; - startsIn = template.startRange; - endsIn = template.endRange; - durationIn = template.duration; - startsOrEndsIn = template.startOrEndRange; - arguments = template.arguments; - return getThis(); - } - - /** - * {@inheritDoc} - */ - public @NotNull - Builder getThis() { - return this; - } - - protected ActivityCreationTemplate fill(final ActivityCreationTemplate template) { - template.startRange = this.startsIn; - template.endRange = this.endsIn; - template.startOrEndRange = this.startsOrEndsIn; - - if (this.type.getDurationType() instanceof DurationType.Uncontrollable) { - if(this.acceptableAbsoluteTimingError.isZero()){ - //TODO: uncomment when precision can be set by user - //logger.warn("Root-finding is likely to fail as activity has an uncontrollable duration and the timing " - //+ "precision is 0. Setting it for you at 1s. Next time, use withTimingPrecision() when building the template."); - this.acceptableAbsoluteTimingError = Duration.of(500, Duration.MILLISECOND); - } - } - - template.type = this.type; - - if (this.durationIn != null) { - template.duration = this.durationIn; - } - //REVIEW: probably want to store permissible rane separate from creation - // default value - - template.arguments = this.arguments; - return template; - } - - /** - * cross-check all specified terms and construct a creation template - * - * creates a new template object based on the conjunction of all of the - * criteria specified so far in this builder, with creation default - * values as specified in this builder to override the activity type - * default values - * - * multiple specifications of the same term sequentially overwrite the - * prior term specification - * - * @return a newly constructed activity creation template that either - * matches activities meeting the conjunction of criteria - * specified or else creates new instances with given defaults - */ - public ActivityCreationTemplate build() { - if (type == null) { - throw new IllegalArgumentException( - "activity creation template requires non-null activity type"); - } - final var template = new ActivityCreationTemplate(); - fill(template); - return template; - } - } - - public record EventWithActivity(Duration start, Duration end, SchedulingActivityDirective activity){} - - public static class HistoryWithActivity{ - List events; - - public HistoryWithActivity(){ - events = new ArrayList<>(); - } - public void add(EventWithActivity event){ - this.events.add(event); - } - public Optional getLastEvent(){ - if(!events.isEmpty()) return Optional.of(events.get(events.size()-1)); - return Optional.empty(); - } - } - /** - * create activity if possible - * - * @param name the activity name - * @param windows the windows in which the activity can be instantiated - * @return the instance of the activity (if successful; else, an empty object) wrapped as an Optional. - */ - 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? - for (var window : windows.iterateEqualTo(true)) { - var act = createInstanceForReal(name, window, facade, plan, planningHorizon, evaluationEnvironment); - if (act.isPresent()) { - return act; - } - } - return Optional.empty(); - } - - private Optional createInstanceForReal(final String name, final Interval interval, SimulationFacade facade, Plan plan, PlanningHorizon planningHorizon, EvaluationEnvironment evaluationEnvironment) { - final var tnw = new TaskNetworkAdapter(new TaskNetwork()); - tnw.addAct(name); - if (interval != null) { - tnw.addEnveloppe(name, "interval", interval.start, interval.end); - } - tnw.addEnveloppe(name, "planningHorizon", planningHorizon.getStartAerie(), planningHorizon.getEndAerie()); - if (this.startRange != null) { - tnw.addStartInterval(name, this.startRange.start, this.startRange.end); - } - if (this.endRange != null) { - tnw.addEndInterval(name, this.endRange.start, this.endRange.end); - } - if (this.duration != null) { - final Optional duration; - try { - duration = this.duration - .evaluate(null, planningHorizon.getHor(), evaluationEnvironment) - .valueAt(Duration.ZERO) - .flatMap($ -> $.asInt().map(i -> Duration.of(i, Duration.MICROSECOND))); - } catch (NullPointerException e) { - throw new UnsupportedOperationException("Activity creation duration arguments cannot depend on simulation results.", e); - } - duration.ifPresent(d -> tnw.addDurationInterval(name, d, d)); - } - final var success = tnw.solveConstraints(); - if (!success) { - logger.warn("Inconsistent temporal constraints, will try next opportunity for activity placement if it exists"); - return Optional.empty(); - } - final var solved = tnw.getAllData(name); - - //the domain of user/scheduling temporal constraints have been reduced with the STN, - //now it is time to find an assignment compatible - //CASE 1: activity has an uncontrollable duration - if(this.type.getDurationType() instanceof DurationType.Uncontrollable){ - final var history = new HistoryWithActivity(); - final var f = new EquationSolvingAlgorithms.Function(){ - //As simulation is called, this is not an approximation - @Override - public boolean isApproximation(){ - return false; - } - - @Override - public Duration valueAt(Duration start, HistoryWithActivity history) { - final var latestConstraintsSimulationResults = getLatestSimulationResults(facade, start); - final var actToSim = SchedulingActivityDirective.of( - type, - start, - null, - SchedulingActivityDirective.instantiateArguments( - arguments, - start, - latestConstraintsSimulationResults, - evaluationEnvironment, - type), - null, - null, - true); - final var lastInsertion = history.getLastEvent(); - Optional computedDuration = Optional.empty(); - final var toRemove = new ArrayList(); - lastInsertion.ifPresent(eventWithActivity -> toRemove.add(eventWithActivity.activity())); - try { - facade.removeAndInsertActivitiesFromSimulation(toRemove, List.of(actToSim)); - computedDuration = facade.getActivityDuration(actToSim); - if(computedDuration.isPresent()) { - history.add(new EventWithActivity(start, start.plus(computedDuration.get()), actToSim)); - } else{ - logger.debug("No simulation error but activity duration could not be found in simulation, likely caused by unfinished activity."); - history.add(new EventWithActivity(start, null, actToSim)); - } - } catch (SimulationFacade.SimulationException e) { - logger.debug("Simulation error while trying to simulate activities: " + e); - history.add(new EventWithActivity(start, null, actToSim)); - } - return computedDuration.map(start::plus).orElse(Duration.MAX_VALUE); - } - - }; - return rootFindingHelper(f, history, solved, facade); - //CASE 2: activity has a controllable duration - } else if (this.type.getDurationType() instanceof DurationType.Controllable dt) { - //select earliest start time, STN guarantees satisfiability - final var earliestStart = solved.start().start; - final var instantiatedArguments = SchedulingActivityDirective.instantiateArguments( - this.arguments, - earliestStart, - getLatestSimulationResults(facade, earliestStart), - evaluationEnvironment, - type); - - final var durationParameterName = dt.parameterName(); - //handle variable duration parameter here - final Duration setActivityDuration; - if (instantiatedArguments.containsKey(durationParameterName)) { - final var argumentDuration = Duration.of( - instantiatedArguments.get(durationParameterName).asInt().get(), - Duration.MICROSECOND); - if (solved.duration().contains(argumentDuration)) { - setActivityDuration = argumentDuration; - } else { - logger.debug( - "Controllable duration set by user is incompatible with temporal constraints associated to the activity template"); - return Optional.empty(); - } - } else { - //REVIEW: should take default duration of activity type maybe ? - setActivityDuration = solved.end().start.minus(solved.start().start); - } - // TODO: When scheduling is allowed to create activities with anchors, this constructor should pull from an expanded creation template - return Optional.of(SchedulingActivityDirective.of( - type, - earliestStart, - setActivityDuration, - SchedulingActivityDirective.instantiateArguments( - this.arguments, - earliestStart, - getLatestSimulationResults(facade, earliestStart), - evaluationEnvironment, - type), - null, - null, - true)); - } else if (this.type.getDurationType() instanceof DurationType.Fixed dt) { - if (!solved.duration().contains(dt.duration())) { - logger.debug("Interval is too small"); - return Optional.empty(); - } - - final var earliestStart = solved.start().start; - - // TODO: When scheduling is allowed to create activities with anchors, this constructor should pull from an expanded creation template - return Optional.of(SchedulingActivityDirective.of( - type, - earliestStart, - dt.duration(), - SchedulingActivityDirective.instantiateArguments( - this.arguments, - earliestStart, - getLatestSimulationResults(facade, earliestStart), - evaluationEnvironment, - type), - null, - null, - true)); - } else if (this.type.getDurationType() instanceof DurationType.Parametric dt) { - final var history = new HistoryWithActivity(); - final var f = new EquationSolvingAlgorithms.Function() { - @Override - public boolean isApproximation(){ - return false; - } - - @Override - public Duration valueAt(final Duration start, final HistoryWithActivity history) { - final var instantiatedArgs = SchedulingActivityDirective.instantiateArguments( - arguments, - start, - getLatestSimulationResults(facade, start), - evaluationEnvironment, - type - ); - - try { - final var duration = dt.durationFunction().apply(instantiatedArgs); - final var activity = SchedulingActivityDirective.of(type,start, - duration, - instantiatedArgs, - null, - null, - true); - history.add(new EventWithActivity(start, start.plus(duration), activity)); - return duration.plus(start); - } catch (InstantiationException e) { - logger.error("Cannot instantiate parameterized duration activity type: " + type.getName()); - throw new RuntimeException(e); - } - } - }; - - return rootFindingHelper(f, history, solved, facade); - } else { - throw new UnsupportedOperationException("Unsupported duration type found: " + this.type.getDurationType()); - } - } - - /** - * generate a new activity instance based on template defaults - * - * used by scheduling logic to instance a new activity that can satisfy - * all of the template criteria in the case another matching activity - * could not be found - * - * uses any defaults specified in the template to override the otherwise - * prevailing defaults from the activity type itself - * - * @param name IN the activity instance identifier to associate to the - * newly constructed activity instance - * @return a newly constructed activity instance with values chosen - * according to any specified template criteria - */ - public @NotNull - Optional createActivity(String name, SimulationFacade facade, Plan plan, PlanningHorizon planningHorizon, EvaluationEnvironment evaluationEnvironment) { - return createInstanceForReal(name,null, facade, plan, planningHorizon, evaluationEnvironment); - } - - private Optional rootFindingHelper( - final EquationSolvingAlgorithms.Function f, - final HistoryWithActivity history, - final TaskNetworkAdapter.TNActData solved, - final SimulationFacade simulationFacade - ) { - try { - var endInterval = solved.end(); - var startInterval = solved.start(); - - final var durationHalfEndInterval = endInterval.duration().dividedBy(2); - - final var result = new EquationSolvingAlgorithms - .SecantDurationAlgorithm() - .findRoot( - f, - history, - startInterval.start, - startInterval.end, - endInterval.start.plus(durationHalfEndInterval), - durationHalfEndInterval, - durationHalfEndInterval, - startInterval.start, - startInterval.end, - 20); - - // 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(); - return Optional.of(lastActivityTested.get().activity); - } catch (EquationSolvingAlgorithms.ZeroDerivativeException zeroOrInfiniteDerivativeException) { - logger.debug("Rootfinding encountered a zero-derivative"); - } catch (EquationSolvingAlgorithms.InfiniteDerivativeException infiniteDerivativeException) { - logger.debug("Rootfinding encountered an infinite-derivative"); - } catch (EquationSolvingAlgorithms.DivergenceException e) { - logger.debug("Rootfinding diverged"); - } catch (EquationSolvingAlgorithms.ExceededMaxIterationException e) { - logger.debug("Too many iterations"); - } catch (EquationSolvingAlgorithms.NoSolutionException e) { - logger.debug("No solution"); - } - if(!history.events.isEmpty()) { - try { - simulationFacade.removeActivitiesFromSimulation(List.of(history.getLastEvent().get().activity())); - } catch (SimulationFacade.SimulationException e) { - throw new RuntimeException("Exception while simulating original plan after activity insertion failure" ,e); - } - } - return Optional.empty(); - } - - private SimulationResults getLatestSimulationResults(final SimulationFacade facade, final Duration until){ - final var latestConstraintsSimulationResults = facade.getLatestConstraintSimulationResults(); - if(latestConstraintsSimulationResults.isEmpty()){ - try { - facade.computeSimulationResultsUntil(until); - return facade.getLatestConstraintSimulationResults().get(); - } catch(SimulationFacade.SimulationException e){ - throw new RuntimeException("Simulation is in irreparable state"); - } - } else{ - return latestConstraintsSimulationResults.get(); - } - } - -} diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityCreationTemplateDisjunction.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityCreationTemplateDisjunction.java deleted file mode 100644 index 3dd2e1072a..0000000000 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityCreationTemplateDisjunction.java +++ /dev/null @@ -1,159 +0,0 @@ -package gov.nasa.jpl.aerie.scheduler.constraints.activities; - -import gov.nasa.jpl.aerie.constraints.model.EvaluationEnvironment; -import gov.nasa.jpl.aerie.constraints.model.SimulationResults; -import gov.nasa.jpl.aerie.constraints.time.Windows; -import gov.nasa.jpl.aerie.scheduler.model.SchedulingActivityDirective; -import gov.nasa.jpl.aerie.scheduler.NotNull; -import gov.nasa.jpl.aerie.scheduler.model.Plan; -import gov.nasa.jpl.aerie.scheduler.model.PlanningHorizon; -import gov.nasa.jpl.aerie.scheduler.simulation.SimulationFacade; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -public class ActivityCreationTemplateDisjunction extends ActivityCreationTemplate { - - - List activityCreationTemplates; - - protected ActivityCreationTemplateDisjunction(List activityCreationTemplates) { - assert (activityCreationTemplates.size() > 0); - this.activityCreationTemplates = new ArrayList<>(activityCreationTemplates); - } - - @Override - @SuppressWarnings("unchecked") - public , AT extends ActivityExpression> AbstractBuilder getNewBuilder() { - return (AbstractBuilder) new OrBuilder(); - } - - /** - * generate a new activity instance based on template defaults - * - * @param name IN the activity instance identifier to associate to the - * newly constructed activity instance - * @return a newly constructed activity instance with values chosen - * according to any specified template criteria, the first of the disjunction - */ - @Override - public @NotNull - Optional createActivity(String name, SimulationFacade facade, Plan plan, PlanningHorizon planningHorizon, EvaluationEnvironment evaluationEnvironment) { - //TODO: returns first ACT of disjunction, change it - return activityCreationTemplates.get(0).createActivity(name, facade, plan, planningHorizon, evaluationEnvironment); - - } - - /** - * generate a new activity instance based on template defaults - * - * @param name IN the activity instance identifier to associate to the - * newly constructed activity instance - * @return a newly constructed activity instance with values chosen - * according to any specified template criteria, the first of the disjunction - */ - @Override - public @NotNull - Optional createActivity(String name, Windows windows, SimulationFacade facade, Plan plan, PlanningHorizon planningHorizon, EvaluationEnvironment evaluationEnvironment) { - for(var act : activityCreationTemplates) { - final var activityCreation = act.createActivity(name, windows, facade, plan, planningHorizon, evaluationEnvironment); - if(activityCreation.isPresent()){ - return activityCreation; - } - } - return Optional.empty(); - } - - /** - * @param act IN the activity to evaluate against the template criteria. - * not null. - * @return true if the act instance matches one of the activity expression of the disjunction - */ - @Override - public boolean matches(@NotNull SchedulingActivityDirective act, SimulationResults simulationResults, EvaluationEnvironment evaluationEnvironment) { - for (var expr : activityCreationTemplates) { - if (expr.matches(act, simulationResults, evaluationEnvironment)) { - return true; - } - } - return false; - } - - /** - * Builder for creating disjunction of activity creation templates - */ - public static class OrBuilder extends - AbstractBuilder - { - - /** - * {@inheritDoc} - */ - public @NotNull - OrBuilder getThis() { - return this; - } - - /** - * bootstraps a new query builder based on existing template - * - * the new builder may then be modified without impacting the existing - * template criteria, eg by adding additional new terms or replacing - * existing terms - * - * @param template IN the template whose criteria should be duplicated - * into this builder. must not be null. - * @return the same builder object updated with new criteria - */ - @Override - public @NotNull - OrBuilder basedOn(@NotNull ActivityCreationTemplateDisjunction template) { - type = template.type; - startsIn = template.startRange; - endsIn = template.endRange; - durationIn = template.duration; - startsOrEndsIn = template.startOrEndRange; - arguments = template.arguments; - activityCreationTemplates = template.activityCreationTemplates; - return getThis(); - } - - - @Override - public ActivityCreationTemplateDisjunction build() { - for (var expr : activityCreationTemplates) { - if (type != null) { - expr.type = type; - } - if (startsIn != null) { - expr.startRange = startsIn; - } - if (endsIn != null) { - expr.endRange = endsIn; - } - if (durationIn != null) { - expr.duration = durationIn; - } - if (startsOrEndsIn != null) { - expr.startOrEndRange = startsOrEndsIn; - } - if (arguments.size() > 0) { - expr.arguments = arguments; - } - } - - return new ActivityCreationTemplateDisjunction(activityCreationTemplates); - } - - protected boolean orBuilder = false; - - List activityCreationTemplates = new ArrayList<>(); - - public OrBuilder or(ActivityCreationTemplate expr) { - activityCreationTemplates.add(expr); - return getThis(); - } - } - -} diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityExpression.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityExpression.java index 01f482a652..eda8c778d9 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityExpression.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityExpression.java @@ -18,6 +18,7 @@ import gov.nasa.jpl.aerie.scheduler.model.ActivityType; import gov.nasa.jpl.aerie.scheduler.NotNull; import gov.nasa.jpl.aerie.scheduler.Nullable; +import org.apache.commons.lang3.tuple.Pair; import java.math.BigDecimal; import java.util.HashMap; @@ -43,24 +44,25 @@ * * templates may be fluently constructed via builders that parse like first * order logic predicate clauses, used in building up scheduling rules + * @param startOrEndRange + * @param startRange range of allowed values for matching activity scheduled start times activities with null start time do not match any non-null range + * The range itself determines if endpoints are inclusive or exclusive + * @param endRange Range of allowed values for matching activity scheduled end times. Activities with null start time do not match any non-null range. + * @param durationRange Range of allowed values for duration of matching activities. + * @param type activity type + * @param nameRe regular expression of matching activity instance names + * @param arguments arguments of matching activities. */ -public class ActivityExpression implements Expression { +public record ActivityExpression( + Windows startOrEndRange, + Interval startRange, + Interval endRange, + Pair>, Expression>> durationRange, + ActivityType type, + java.util.regex.Pattern nameRe, + Map> arguments +) implements Expression { - private Windows startOrEndRangeW; - - @SuppressWarnings("unchecked") - public , AT extends ActivityExpression> AbstractBuilder getNewBuilder() { - return (AbstractBuilder) new Builder(); - } - - /** - * ctor is private to prevent inconsistent construction - * - * please use the enclosed fluent Builder class instead - * - * leaves all criteria elements unspecified - */ - protected ActivityExpression() { } /** * a fluent builder class for constructing consistent template queries @@ -77,29 +79,32 @@ protected ActivityExpression() { } * the builder checks for consistency among all specified terms at least by * the final build() call * - * @param concrete builder type, used to ensure right builder returned - * by each chained operation (ref curiously recuring template - * pattern) - * @param concrete activity template type constructed by the builder */ - public abstract static class AbstractBuilder, AT extends ActivityExpression> { - + public static class Builder { protected Duration acceptableAbsoluteTimingError = Duration.of(0, Duration.MILLISECOND); Map> arguments = new HashMap<>(); + protected @Nullable + ActivityType type; + protected @Nullable Interval startsIn; + protected @Nullable Windows startsOrEndsIn; + protected @Nullable Interval endsIn; + protected Windows startsInR; + protected @Nullable Pair>, Expression>> durationIn; + protected java.util.regex.Pattern nameRe; - public B withArgument(String argument, SerializedValue val) { + public Builder withArgument(String argument, SerializedValue val) { arguments.put(argument, new ProfileExpression<>(new DiscreteValue(val))); return getThis(); } - public B withArgument(String argument, ProfileExpression val) { + public Builder withArgument(String argument, ProfileExpression val) { arguments.put(argument, val); return getThis(); } - public B withTimingPrecision(Duration acceptableAbsoluteTimingError){ + public Builder withTimingPrecision(Duration acceptableAbsoluteTimingError){ this.acceptableAbsoluteTimingError = acceptableAbsoluteTimingError; return getThis(); } @@ -115,15 +120,11 @@ public B withTimingPrecision(Duration acceptableAbsoluteTimingError){ * if no specific type is required * @return the same builder object updated with new criteria */ - public @NotNull - B ofType(@Nullable ActivityType type) { + public @NotNull Builder ofType(@Nullable ActivityType type) { this.type = type; return getThis(); } - protected @Nullable - ActivityType type; - /** * requires activities have a scheduled start time in a specified range * @@ -135,13 +136,11 @@ B ofType(@Nullable ActivityType type) { * inclusive or exclusive at its end points * @return the same builder object updated with new criteria */ - public @NotNull - B startsIn(@Nullable Interval range) { + public @NotNull Builder startsIn(@Nullable Interval range) { this.startsIn = extendUpToAbsoluteError(range, acceptableAbsoluteTimingError); return getThis(); } - protected @Nullable Interval startsIn; /** * requires activities have a scheduled start or end time in a specified range @@ -154,13 +153,11 @@ B startsIn(@Nullable Interval range) { * inclusive or exclusive at its end points * @return the same builder object updated with new criteria */ - public @NotNull - B startsOrEndsIn(@Nullable Interval range) { - this.startsOrEndsIn = extendUpToAbsoluteError(range, acceptableAbsoluteTimingError); + public @NotNull Builder startsOrEndsIn(@Nullable Interval range) { + this.startsOrEndsIn = new Windows(false).set(extendUpToAbsoluteError(range, acceptableAbsoluteTimingError), true); return getThis(); } - protected @Nullable Interval startsOrEndsIn; /** * requires activities have a scheduled start or end time in a specified range @@ -173,18 +170,15 @@ B startsOrEndsIn(@Nullable Interval range) { * inclusive or exclusive at its end points * @return the same builder object updated with new criteria */ - public @NotNull - B startsOrEndsIn(@Nullable Windows windows) { + public @NotNull Builder startsOrEndsIn(@Nullable Windows windows) { Windows wins = new Windows(false); for(final var win : windows.iterateEqualTo(true)){ wins = wins.set(extendUpToAbsoluteError(win, acceptableAbsoluteTimingError), true); } - this.startsOrEndsInW = wins; + this.startsOrEndsIn = wins; return getThis(); } - protected @Nullable - Windows startsOrEndsInW; /** * requires activities have a scheduled end time in a specified range @@ -197,16 +191,13 @@ B startsOrEndsIn(@Nullable Windows windows) { * inclusive or exclusive at its end points * @return the same builder object updated with new criteria */ - public @NotNull - B endsIn(@Nullable Interval range) { + public @NotNull Builder endsIn(@Nullable Interval range) { this.endsIn = extendUpToAbsoluteError(range, acceptableAbsoluteTimingError); return getThis(); } - protected @Nullable Interval endsIn; - public @NotNull - B startsIn(Windows ranges) { + public @NotNull Builder startsIn(Windows ranges) { Windows wins = new Windows(false); for(final var win : ranges.iterateEqualTo(true)) { wins = wins.set(extendUpToAbsoluteError(win, acceptableAbsoluteTimingError), true); @@ -215,10 +206,9 @@ B startsIn(Windows ranges) { return getThis(); } - protected Windows startsInR; /** - * requires activities have a simulated duration in a specified range + * requires activities have a simulated duration in a specified value * * activities without a concrete simulated duration will not match * @@ -228,30 +218,27 @@ B startsIn(Windows ranges) { * inclusive or exclusive at its end points * @return the same builder object updated with new criteria */ - public @NotNull - B durationIn(@Nullable Duration duration) { + public @NotNull Builder durationIn(@Nullable Duration duration) { if (duration == null) { - this.durationIn = Expression.of(() -> new DiscreteProfile()); + this.durationIn = Pair.of(Expression.of(() -> new DiscreteProfile()), Expression.of(() -> new DiscreteProfile())); } - this.durationIn = new DiscreteProfileFromDuration(new DurationLiteral(duration)); + this.durationIn = Pair.of(new DiscreteProfileFromDuration(new DurationLiteral(duration)), new DiscreteProfileFromDuration(new DurationLiteral(duration))); return getThis(); } - protected @Nullable Expression> durationIn; /** - * bootstraps a new query builder based on existing template + * requires activities have a simulated duration at a specified value * - * the new builder may then be modified without impacting the existing - * template criteria, eg by adding additional new terms or replacing - * existing terms + * activities without a concrete simulated duration will not match * - * @param template IN the template whose criteria should be duplicated - * into this builder. must not be null. + * @param durationExpression IN STORED the allowed duration. * @return the same builder object updated with new criteria */ - public abstract @NotNull - B basedOn(@NotNull AT template); + public @NotNull Builder durationIn(Expression> durationExpression) { + this.durationIn = Pair.of(durationExpression, durationExpression); + return getThis(); + } /** * bootstraps a new query builder based on an existing activity instance @@ -264,8 +251,7 @@ B durationIn(@Nullable Duration duration) { * prototype for the new search criteria. must not be null. * @return the same builder object updated with new criteria */ - public @NotNull - B basedOn(@NotNull SchedulingActivityDirective existingAct) { + public @NotNull Builder basedOn(@NotNull SchedulingActivityDirective existingAct) { type = existingAct.getType(); if (existingAct.startOffset() != null) { @@ -275,35 +261,9 @@ B basedOn(@NotNull SchedulingActivityDirective existingAct) { durationIn(existingAct.duration()); return getThis(); - - //FINISH: extract all param values as == criteria } - /** - * returns this builder object for further chaining, typed at concrete level - * - * @return the concrete builder type object for further method chaining - */ - public abstract @NotNull - B getThis(); - /** - * collect and cross-check all specified terms and construct the template - * - * creates a new template object based on a conjunction of all of the - * criteria specified so far in this builder - * - * multiple specifications of the same term sequentially overwrite the - * prior term specification - * - * the terms are checked for high level self-consistency, but it is still - * possible to construct predicates that will never match any activities - * - * @return a newly constructed template that matches activities meeting - * the conjunction of all criteria specified to the builder - */ - public abstract @NotNull - AT build(); private Interval extendUpToAbsoluteError(final Interval interval, final Duration absoluteError){ final var diff = absoluteError.times(2).minus(interval.duration()); @@ -315,15 +275,6 @@ private Interval extendUpToAbsoluteError(final Interval interval, final Duration } } - } - - /** - * {@inheritDoc} - * - * concrete builder used to create instances of ActivityTemplate (and not a more - * specific type like ActivityCreationTemplate) - */ - public static class Builder extends AbstractBuilder { /** * {@inheritDoc} @@ -333,123 +284,46 @@ Builder getThis() { return this; } - @Override public @NotNull Builder basedOn(@NotNull ActivityExpression template) { type = template.type; startsIn = template.startRange; endsIn = template.endRange; - durationIn = template.duration; + durationIn = template.durationRange; startsOrEndsIn = template.startOrEndRange; - startsOrEndsInW = template.startOrEndRangeW; arguments = template.arguments; return getThis(); } - - protected ActivityExpression fill(ActivityExpression template) { - template.type = type; - template.startRange = startsIn; - template.endRange = endsIn; - template.duration = durationIn; - template.startOrEndRange = startsOrEndsIn; - template.startOrEndRangeW = startsOrEndsInW; - template.arguments = arguments; - return template; - } - /** - * {@inheritDoc} + * collect and cross-check all specified terms and construct the template + * + * creates a new template object based on a conjunction of all of the + * criteria specified so far in this builder + * + * multiple specifications of the same term sequentially overwrite the + * prior term specification + * + * the terms are checked for high level self-consistency, but it is still + * possible to construct predicates that will never match any activities + * + * @return a newly constructed template that matches activities meeting + * the conjunction of all criteria specified to the builder */ - public @NotNull - ActivityExpression build() { - final var template = new ActivityExpression(); - fill(template); + public @NotNull ActivityExpression build() { + final var template = new ActivityExpression( + startsOrEndsIn, + startsIn, + endsIn, + durationIn, + type, + nameRe, + arguments + ); return template; } - } - - /** - * range of allowed values for matching activity scheduled start times - * - * activities with null start time do not match any non-null range - * - * null if no limit on start time - * - * the range itself determines if endpoints are inclusive or exclusive - */ - protected @Nullable Interval startRange; - - /** - * range of allowed values for matching activity scheduled end times - * - * activities with null start time do not match any non-null range - * - * null if no limit on start time - * - * the range itself determines if endpoints are inclusive or exclusive - */ - protected @Nullable Interval endRange; - /** - * range of allowed values for matching activity scheduled end times - * - * activities with null start time do not match any non-null range - * - * null if no limit on start time - * - * the range itself determines if endpoints are inclusive or exclusive - */ - protected @Nullable Interval startOrEndRange; - - - /** - * range of allowed values for matching activity simulated durations - * - * activities with null duration do not match any non-null range - * - * null if no limit on duration - * - * the range itself determines if endpoints are inclusive or exclusive - */ - protected @Nullable Expression> duration; - - /** - * the bounding super-type for matching activities - * - * activities with types derived from target type also match - * - * null if no limit on activity type - */ - protected @Nullable - ActivityType type; - - /** - * regular expression of matching activity instance names - * - * activities with null names do not match any non-null regular expression - * - * null if no limit on activity instance name - */ - protected @Nullable java.util.regex.Pattern nameRE; - - /** - * fetch the range of allowed starting times matched by this template - * - * @return the allowed range of start times for matching activities, or null - * if no limit on start time - */ - public @Nullable - Interval getStartRange() { return startRange; } - - /** - * fetch the bounding super type of activities matched by this template - * - * @return the super type for matching activities, or null if no limit on - * activity type - */ - public @Nullable - ActivityType getType() { return type; } + } /** * creates a template matching a given activity type (or its subtypes) @@ -463,13 +337,11 @@ ActivityExpression build() { * @return an activity template that matches only activities with the * specified super type */ - public static @NotNull - ActivityExpression ofType(@NotNull ActivityType type) { + public static @NotNull ActivityExpression ofType(@NotNull ActivityType type) { return new Builder().ofType(type).build(); } - Map> arguments = new HashMap<>(); /** @@ -498,14 +370,7 @@ public boolean matches(@NotNull SchedulingActivityDirective act, SimulationResul final var startT = act.startOffset(); final var endT = act.getEndTime(); match = - ((startT != null) && startOrEndRange.contains(startT)) || (endT != null) && startOrEndRange.contains(endT); - } - - if (match && startOrEndRangeW != null) { - final var startT = act.startOffset(); - final var endT = act.getEndTime(); - match = ((startT != null) && startOrEndRangeW.includes(Interval.at(startT)) - || (endT != null) && startOrEndRangeW.includes(Interval.at(endT))); + ((startT != null) && startOrEndRange.includes(Interval.at(startT))) || (endT != null) && startOrEndRange.includes(Interval.at(endT)); } if (match && endRange != null) { @@ -513,13 +378,17 @@ public boolean matches(@NotNull SchedulingActivityDirective act, SimulationResul match = (endT != null) && endRange.contains(endT); } - if (match && duration != null) { + if (match && durationRange != null) { final var dur = act.duration(); - final Optional durRequirement = this.duration + final Optional durRequirementLower = this.durationRange.getLeft() .evaluate(simulationResults, evaluationEnvironment) .valueAt(Duration.ZERO) .flatMap($ -> $.asInt().map(i -> Duration.of(i, Duration.MICROSECOND))); - match = durRequirement.isEmpty() || (dur != null && durRequirement.get().isEqualTo(dur)); + final Optional durRequirementUpper = this.durationRange.getRight() + .evaluate(simulationResults, evaluationEnvironment) + .valueAt(Duration.ZERO) + .flatMap($ -> $.asInt().map(i -> Duration.of(i, Duration.MICROSECOND))); + match = durRequirementLower.isEmpty() || (dur != null && durRequirementLower.get().noLongerThan(dur) && durRequirementUpper.get().noShorterThan(dur)); } //activity must have all instantiated arguments of template to be compatible @@ -553,14 +422,7 @@ public boolean matches(@NotNull gov.nasa.jpl.aerie.constraints.model.ActivityIns final var startT = act.interval.start; final var endT = act.interval.end; match = - ((startT != null) && startOrEndRange.contains(startT)) || (endT != null) && startOrEndRange.contains(endT); - } - - if (match && startOrEndRangeW != null) { - final var startT = act.interval.start; - final var endT = act.interval.end; - match = ((startT != null) && startOrEndRangeW.includes(Interval.at(startT)) - || (endT != null) && startOrEndRangeW.includes(Interval.at(endT))); + ((startT != null) && startOrEndRange.includes(Interval.at(startT)) || (endT != null) && startOrEndRange.includes(Interval.at(endT))); } if (match && endRange != null) { @@ -568,13 +430,17 @@ public boolean matches(@NotNull gov.nasa.jpl.aerie.constraints.model.ActivityIns match = (endT != null) && endRange.contains(endT); } - if (match && duration != null) { + if (match && durationRange != null) { final var dur = act.interval.duration(); - final Optional durRequirement = this.duration + final Optional durRequirementLower = this.durationRange.getLeft() + .evaluate(simulationResults, evaluationEnvironment) + .valueAt(Duration.ZERO) + .flatMap($ -> $.asInt().map(i -> Duration.of(i, Duration.MICROSECOND))); + final Optional durRequirementUpper = this.durationRange.getRight() .evaluate(simulationResults, evaluationEnvironment) .valueAt(Duration.ZERO) .flatMap($ -> $.asInt().map(i -> Duration.of(i, Duration.MICROSECOND))); - match = durRequirement.isEmpty() || (dur != null && durRequirement.get() == dur); + match = durRequirementUpper.isEmpty() || (dur != null && dur.noShorterThan(durRequirementLower.get()) && dur.noLongerThan(durRequirementUpper.get())); } //activity must have all instantiated arguments of template to be compatible diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityExpressionDisjunction.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityExpressionDisjunction.java deleted file mode 100644 index 3c4c02c4e8..0000000000 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/activities/ActivityExpressionDisjunction.java +++ /dev/null @@ -1,78 +0,0 @@ -package gov.nasa.jpl.aerie.scheduler.constraints.activities; - -import gov.nasa.jpl.aerie.constraints.model.EvaluationEnvironment; -import gov.nasa.jpl.aerie.constraints.model.SimulationResults; -import gov.nasa.jpl.aerie.scheduler.model.SchedulingActivityDirective; -import gov.nasa.jpl.aerie.scheduler.NotNull; - -import java.util.ArrayList; -import java.util.List; - -public class ActivityExpressionDisjunction extends ActivityExpression { - - - final List actExpressions; - - protected ActivityExpressionDisjunction(List actExpressions) { - this.actExpressions = new ArrayList<>(actExpressions); - } - - - @SuppressWarnings("unchecked") - public , AT extends ActivityExpression> AbstractBuilder getNewBuilder() { - return (AbstractBuilder) new OrBuilder(); - } - - /** - * @param act IN the activity to evaluate against the template criteria. - * not null. - * @return true if the act instance matches one of the activity expression of the disjunction - */ - @Override - public boolean matches(@NotNull SchedulingActivityDirective act, SimulationResults simulationResults, EvaluationEnvironment evaluationEnvironment) { - for (var expr : actExpressions) { - if (expr.matches(act, simulationResults, evaluationEnvironment)) { - return true; - } - } - - return false; - } - - public static class OrBuilder extends AbstractBuilder { - - @Override - public OrBuilder basedOn(ActivityExpressionDisjunction template) { - type = template.type; - startsIn = template.startRange; - endsIn = template.endRange; - durationIn = template.duration; - startsOrEndsIn = template.startOrEndRange; - arguments = template.arguments; - exprs = template.actExpressions; - return getThis(); - } - - /** - * {@inheritDoc} - */ - public @NotNull - OrBuilder getThis() { - return this; - } - - @Override - public ActivityExpressionDisjunction build() { - return new ActivityExpressionDisjunction(exprs); - } - - List exprs = new ArrayList<>(); - - public OrBuilder or(ActivityExpression expr) { - exprs.add(expr); - return getThis(); - } - } - - -} diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/ActivityTemplateGoal.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/ActivityTemplateGoal.java index 6fe0900084..6c84075509 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/ActivityTemplateGoal.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/ActivityTemplateGoal.java @@ -3,7 +3,6 @@ import gov.nasa.jpl.aerie.constraints.model.EvaluationEnvironment; import gov.nasa.jpl.aerie.constraints.time.Windows; import gov.nasa.jpl.aerie.constraints.tree.Expression; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.model.Plan; import gov.nasa.jpl.aerie.scheduler.model.PlanningHorizon; @@ -31,7 +30,7 @@ public abstract static class Builder> extends ActivityExist * with the ability to create new instances if necessary * @return this builder, ready for additional specification */ - public T thereExistsOne(ActivityCreationTemplate template) { + public T thereExistsOne(ActivityExpression template) { thereExists = template; return getThis(); } @@ -41,7 +40,7 @@ public T match(ActivityExpression expression){ return getThis(); } - protected ActivityCreationTemplate thereExists; + protected ActivityExpression thereExists; protected ActivityExpression matchingActTemplate; /** @@ -83,7 +82,7 @@ protected ActivityTemplateGoal fill(ActivityTemplateGoal goal) { }//Builder - public ActivityCreationTemplate getActTemplate() { + public ActivityExpression getActTemplate() { return desiredActTemplate; } @@ -97,23 +96,7 @@ public ActivityCreationTemplate getActTemplate() { * the goal's created activity type */ public Expression getActivityStateConstraints() { - return desiredActTemplate.getType().getStateConstraints(); - } - - /** - * creates a new activity instance that will increase this goal's satisfaction - * - * the activity is labeled as being created for this goal - * - * the activity is not inserted into any plan yet - * - * @return a new activity instance that will improve the satisfaction of - * this goal if it were inserted into a plan - */ - public Optional createActivity(SimulationFacade facade, Plan plan, PlanningHorizon planningHorizon, EvaluationEnvironment evaluationEnvironment) { - //REVIEW: uuid probably overkill. random is especially bad for repeatability. - final var actName = getName() + "_" + java.util.UUID.randomUUID(); - return desiredActTemplate.createActivity(actName, facade, plan, planningHorizon, evaluationEnvironment); + return desiredActTemplate.type().getStateConstraints(); } /** @@ -126,7 +109,7 @@ protected ActivityTemplateGoal() { } /** * the pattern used to create new instances if none already exist */ - protected ActivityCreationTemplate desiredActTemplate; + protected ActivityExpression desiredActTemplate; /** * the pattern used to match with satisfying activity instances 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..e99c01b3eb 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 @@ -10,7 +10,6 @@ import gov.nasa.jpl.aerie.scheduler.model.SchedulingActivityDirectiveId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; import gov.nasa.jpl.aerie.scheduler.model.SchedulingActivityDirective; import gov.nasa.jpl.aerie.scheduler.conflicts.Conflict; import gov.nasa.jpl.aerie.scheduler.model.Plan; 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..0626bcc5f7 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 @@ -9,8 +9,6 @@ import gov.nasa.jpl.aerie.scheduler.conflicts.Conflict; import gov.nasa.jpl.aerie.scheduler.conflicts.MissingActivityTemplateConflict; import gov.nasa.jpl.aerie.scheduler.conflicts.MissingAssociationConflict; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplateDisjunction; import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.constraints.durationexpressions.DurationExpression; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeAnchor; @@ -210,16 +208,11 @@ else if (this.initiallyEvaluatedTemporalContext == null) { //the rest is the same if no such bisection has happened final var conflicts = new java.util.LinkedList(); for (var window : anchors) { - boolean disj = false; - ActivityExpression.AbstractBuilder activityFinder = null; - ActivityExpression.AbstractBuilder activityCreationTemplate = null; - if (this.desiredActTemplate instanceof ActivityCreationTemplateDisjunction) { - disj = true; - activityFinder = new ActivityCreationTemplateDisjunction.OrBuilder(); - activityCreationTemplate = new ActivityCreationTemplateDisjunction.OrBuilder(); - } else if (this.desiredActTemplate != null) { + ActivityExpression.Builder activityFinder = null; + ActivityExpression.Builder activityCreationTemplate = null; + if (this.desiredActTemplate != null) { activityFinder = new ActivityExpression.Builder(); - activityCreationTemplate = new ActivityCreationTemplate.Builder(); + activityCreationTemplate = new ActivityExpression.Builder(); } assert activityFinder != null; activityFinder.basedOn(this.matchActTemplate); @@ -267,12 +260,8 @@ else if (this.initiallyEvaluatedTemporalContext == null) { } } - ActivityCreationTemplate temp; - if (disj) { - temp = (ActivityCreationTemplateDisjunction) activityCreationTemplate.build(); - } else { - temp = (ActivityCreationTemplate) activityCreationTemplate.build(); - } + final var temp = activityCreationTemplate.build(); + if (!alreadyOneActivityAssociated) { //create conflict if no matching target activity found if (existingActs.isEmpty()) { 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..270efa6564 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 @@ -113,7 +113,7 @@ public java.util.Collection getConflicts(@NotNull Plan plan, final Sim final var windows = tempWindowPlanHorizon.and(this.getTemporalContext().evaluate(simulationResults, evaluationEnvironment)); //check repeat is larger than activity duration - if(this.getActTemplate().getType().getDurationType() instanceof DurationType.Fixed act){ + if(this.getActTemplate().type().getDurationType() instanceof DurationType.Fixed act){ boolean durActVSRec = act.duration().longerThan(this.recurrenceInterval.min); if(durActVSRec){ throw new UnexpectedTemporalContextChangeException("The goal is unsatisfiable as its duration is longer than the repeat duration"); diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/SchedulingCondition.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/SchedulingCondition.java index 820b38320f..29903aeb01 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/SchedulingCondition.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/model/SchedulingCondition.java @@ -29,7 +29,7 @@ public Windows findWindows( if (conflict instanceof MissingActivityInstanceConflict c) { type = c.getInstance().getType(); } else if (conflict instanceof MissingActivityTemplateConflict c) { - type = c.getActTemplate().getType(); + type = c.getActTemplate().type(); } else { throw new Error("Unsupported conflict %s".formatted(conflict)); } 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..469bb44489 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 @@ -7,11 +7,16 @@ import gov.nasa.jpl.aerie.constraints.time.Windows; import gov.nasa.jpl.aerie.constraints.tree.Expression; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; +import gov.nasa.jpl.aerie.merlin.protocol.types.DurationType; +import gov.nasa.jpl.aerie.merlin.protocol.types.InstantiationException; +import gov.nasa.jpl.aerie.scheduler.EquationSolvingAlgorithms; +import gov.nasa.jpl.aerie.scheduler.NotNull; import gov.nasa.jpl.aerie.scheduler.conflicts.Conflict; import gov.nasa.jpl.aerie.scheduler.conflicts.MissingActivityConflict; import gov.nasa.jpl.aerie.scheduler.conflicts.MissingActivityInstanceConflict; import gov.nasa.jpl.aerie.scheduler.conflicts.MissingActivityTemplateConflict; import gov.nasa.jpl.aerie.scheduler.conflicts.MissingAssociationConflict; +import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.constraints.scheduling.GlobalConstraint; import gov.nasa.jpl.aerie.scheduler.constraints.scheduling.GlobalConstraintWithIntrospection; import gov.nasa.jpl.aerie.scheduler.goals.ActivityTemplateGoal; @@ -21,6 +26,8 @@ import gov.nasa.jpl.aerie.scheduler.model.*; import gov.nasa.jpl.aerie.scheduler.model.SchedulingActivityDirective; import gov.nasa.jpl.aerie.scheduler.simulation.SimulationFacade; +import gov.nasa.jpl.aerie.scheduler.solver.stn.TaskNetwork; +import gov.nasa.jpl.aerie.scheduler.solver.stn.TaskNetworkAdapter; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,6 +87,23 @@ public class PrioritySolver implements Solver { private final SimulationFacade simulationFacade; + public record EventWithActivity(Duration start, Duration end, SchedulingActivityDirective activity){} + + public static class HistoryWithActivity{ + List events; + + public HistoryWithActivity(){ + events = new ArrayList<>(); + } + public void add(EventWithActivity event){ + this.events.add(event); + } + public Optional getLastEvent(){ + if(!events.isEmpty()) return Optional.of(events.get(events.size()-1)); + return Optional.empty(); + } + } + /** * create a new greedy solver for the specified input planning problem * @@ -720,12 +744,10 @@ 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 - final var act = missingTemplate.getActTemplate().createActivity( + final var act = createOneActivity( + missingTemplate.getActTemplate(), goal.getName() + "_" + java.util.UUID.randomUUID(), startWindows, - simulationFacade, - plan, - this.problem.getPlanningHorizon(), missing.getEvaluationEnvironment()); act.ifPresent(newActs::add); } @@ -825,6 +847,265 @@ private Windows narrowGlobalConstraints( return tmp; } + /** + * creates one activity if possible + * + * @param name the activity name + * @param windows the windows in which the activity can be instantiated + * @return the instance of the activity (if successful; else, an empty object) wrapped as an Optional. + */ + public @NotNull Optional createOneActivity( + final ActivityExpression activityExpression, + final String name, + final Windows windows, + final EvaluationEnvironment evaluationEnvironment) { + //REVIEW: how to properly export any flexibility to instance? + for (var window : windows.iterateEqualTo(true)) { + var activity = instantiateActivity(activityExpression, name, window, evaluationEnvironment); + if (activity.isPresent()) { + return activity; + } + } + return Optional.empty(); + } + private Optional instantiateActivity( + final ActivityExpression activityExpression, + final String name, + final Interval interval, + final EvaluationEnvironment evaluationEnvironment) { + final var planningHorizon = this.problem.getPlanningHorizon(); + final var taskNetwork = new TaskNetworkAdapter(new TaskNetwork()); + taskNetwork.addAct(name); + if (interval != null) { + taskNetwork.addEnveloppe(name, "interval", interval.start, interval.end); + } + taskNetwork.addEnveloppe(name, "planningHorizon", planningHorizon.getStartAerie(), planningHorizon.getEndAerie()); + if (activityExpression.startRange() != null) { + taskNetwork.addStartInterval(name, activityExpression.startRange().start, activityExpression.startRange().end); + } + if (activityExpression.endRange() != null) { + taskNetwork.addEndInterval(name, activityExpression.endRange().start, activityExpression.endRange().end); + } + if (activityExpression.durationRange() != null) { + final Optional durRequirementLower; + final Optional durRequirementUpper; + try { + durRequirementLower = activityExpression.durationRange().getLeft() + .evaluate(null, planningHorizon.getHor(), evaluationEnvironment) + .valueAt(Duration.ZERO) + .flatMap($ -> $.asInt().map(i -> Duration.of(i, Duration.MICROSECOND))); + durRequirementUpper = activityExpression.durationRange().getRight() + .evaluate(null, planningHorizon.getHor(), evaluationEnvironment) + .valueAt(Duration.ZERO) + .flatMap($ -> $.asInt().map(i -> Duration.of(i, Duration.MICROSECOND))); + + } catch (NullPointerException e) { + throw new UnsupportedOperationException("Activity creation duration arguments cannot depend on simulation results.", e); + } + if(durRequirementLower.isPresent() && durRequirementUpper.isPresent()) { + taskNetwork.addDurationInterval(name, durRequirementLower.get(), durRequirementUpper.get()); + } + } + final var success = taskNetwork.solveConstraints(); + if (!success) { + logger.warn("Inconsistent temporal constraints, will try next opportunity for activity placement if it exists"); + return Optional.empty(); + } + final var solved = taskNetwork.getAllData(name); + + //the domain of user/scheduling temporal constraints have been reduced with the STN, + //now it is time to find an assignment compatible + //CASE 1: activity has an uncontrollable duration + if(activityExpression.type().getDurationType() instanceof DurationType.Uncontrollable){ + final var history = new HistoryWithActivity(); + final var f = new EquationSolvingAlgorithms.Function(){ + @Override + public Duration valueAt(Duration start, HistoryWithActivity history) { + final var latestConstraintsSimulationResults = getLatestSimResultsUpTo(start); + final var actToSim = SchedulingActivityDirective.of( + activityExpression.type(), + start, + null, + SchedulingActivityDirective.instantiateArguments( + activityExpression.arguments(), + start, + latestConstraintsSimulationResults, + evaluationEnvironment, + activityExpression.type()), + null, + null, + true); + final var lastInsertion = history.getLastEvent(); + Optional computedDuration = Optional.empty(); + final var toRemove = new ArrayList(); + lastInsertion.ifPresent(eventWithActivity -> toRemove.add(eventWithActivity.activity())); + try { + simulationFacade.removeAndInsertActivitiesFromSimulation(toRemove, List.of(actToSim)); + computedDuration = simulationFacade.getActivityDuration(actToSim); + if(computedDuration.isPresent()) { + history.add(new EventWithActivity(start, start.plus(computedDuration.get()), actToSim)); + } else{ + logger.debug("No simulation error but activity duration could not be found in simulation, likely caused by unfinished activity."); + history.add(new EventWithActivity(start, null, actToSim)); + } + } catch (SimulationFacade.SimulationException e) { + logger.debug("Simulation error while trying to simulate activities: " + e); + history.add(new EventWithActivity(start, null, actToSim)); + } + return computedDuration.map(start::plus).orElse(Duration.MAX_VALUE); + } + + }; + return rootFindingHelper(f, history, solved); + //CASE 2: activity has a controllable duration + } else if (activityExpression.type().getDurationType() instanceof DurationType.Controllable dt) { + //select earliest start time, STN guarantees satisfiability + final var earliestStart = solved.start().start; + final var instantiatedArguments = SchedulingActivityDirective.instantiateArguments( + activityExpression.arguments(), + earliestStart, + getLatestSimResultsUpTo(earliestStart), + evaluationEnvironment, + activityExpression.type()); + + final var durationParameterName = dt.parameterName(); + //handle variable duration parameter here + final Duration setActivityDuration; + if (instantiatedArguments.containsKey(durationParameterName)) { + final var argumentDuration = Duration.of( + instantiatedArguments.get(durationParameterName).asInt().get(), + Duration.MICROSECOND); + if (solved.duration().contains(argumentDuration)) { + setActivityDuration = argumentDuration; + } else { + logger.debug( + "Controllable duration set by user is incompatible with temporal constraints associated to the activity template"); + return Optional.empty(); + } + } else { + //REVIEW: should take default duration of activity type maybe ? + setActivityDuration = solved.end().start.minus(solved.start().start); + } + // TODO: When scheduling is allowed to create activities with anchors, this constructor should pull from an expanded creation template + return Optional.of(SchedulingActivityDirective.of( + activityExpression.type(), + earliestStart, + setActivityDuration, + SchedulingActivityDirective.instantiateArguments( + activityExpression.arguments(), + earliestStart, + getLatestSimResultsUpTo(earliestStart), + evaluationEnvironment, + activityExpression.type()), + null, + null, + true)); + } else if (activityExpression.type().getDurationType() instanceof DurationType.Fixed dt) { + if (!solved.duration().contains(dt.duration())) { + logger.debug("Interval is too small"); + return Optional.empty(); + } + + final var earliestStart = solved.start().start; + + // TODO: When scheduling is allowed to create activities with anchors, this constructor should pull from an expanded creation template + return Optional.of(SchedulingActivityDirective.of( + activityExpression.type(), + earliestStart, + dt.duration(), + SchedulingActivityDirective.instantiateArguments( + activityExpression.arguments(), + earliestStart, + getLatestSimResultsUpTo(earliestStart), + evaluationEnvironment, + activityExpression.type()), + null, + null, + true)); + } else if (activityExpression.type().getDurationType() instanceof DurationType.Parametric dt) { + final var history = new HistoryWithActivity(); + final var f = new EquationSolvingAlgorithms.Function() { + @Override + public Duration valueAt(final Duration start, final HistoryWithActivity history) { + final var instantiatedArgs = SchedulingActivityDirective.instantiateArguments( + activityExpression.arguments(), + start, + getLatestSimResultsUpTo(start), + evaluationEnvironment, + activityExpression.type() + ); + + try { + final var duration = dt.durationFunction().apply(instantiatedArgs); + final var activity = SchedulingActivityDirective.of(activityExpression.type(),start, + duration, + instantiatedArgs, + null, + null, + true); + history.add(new EventWithActivity(start, start.plus(duration), activity)); + return duration.plus(start); + } catch (InstantiationException e) { + logger.error("Cannot instantiate parameterized duration activity type: " + activityExpression.type().getName()); + throw new RuntimeException(e); + } + } + }; + + return rootFindingHelper(f, history, solved); + } else { + throw new UnsupportedOperationException("Unsupported duration type found: " + activityExpression.type().getDurationType()); + } + } + + private Optional rootFindingHelper( + final EquationSolvingAlgorithms.Function f, + final HistoryWithActivity history, + final TaskNetworkAdapter.TNActData solved) { + try { + var endInterval = solved.end(); + var startInterval = solved.start(); + + final var durationHalfEndInterval = endInterval.duration().dividedBy(2); + + final var result = new EquationSolvingAlgorithms + .SecantDurationAlgorithm() + .findRoot( + f, + history, + startInterval.start, + startInterval.end, + endInterval.start.plus(durationHalfEndInterval), + durationHalfEndInterval, + durationHalfEndInterval, + startInterval.start, + startInterval.end, + 20); + + // 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(); + return Optional.of(lastActivityTested.get().activity); + } catch (EquationSolvingAlgorithms.ZeroDerivativeException zeroOrInfiniteDerivativeException) { + logger.debug("Rootfinding encountered a zero-derivative"); + } catch (EquationSolvingAlgorithms.InfiniteDerivativeException infiniteDerivativeException) { + logger.debug("Rootfinding encountered an infinite-derivative"); + } catch (EquationSolvingAlgorithms.DivergenceException e) { + logger.debug("Rootfinding diverged"); + } catch (EquationSolvingAlgorithms.ExceededMaxIterationException e) { + logger.debug("Too many iterations"); + } catch (EquationSolvingAlgorithms.NoSolutionException e) { + logger.debug("No solution"); + } + if(!history.events.isEmpty()) { + try { + simulationFacade.removeActivitiesFromSimulation(List.of(history.getLastEvent().get().activity())); + } catch (SimulationFacade.SimulationException e) { + throw new RuntimeException("Exception while simulating original plan after activity insertion failure" ,e); + } + } + return Optional.empty(); + } + public void printEvaluation() { logger.warn("Remaining conflicts for goals "); for (var goalEval : evaluation.getGoals()) { diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/FixedDurationTest.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/FixedDurationTest.java index 5eff9d1f60..53cddc69d7 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/FixedDurationTest.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/FixedDurationTest.java @@ -6,7 +6,7 @@ import gov.nasa.jpl.aerie.constraints.tree.WindowsWrapperExpression; import gov.nasa.jpl.aerie.merlin.driver.MissionModel; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; +import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeExpression; import gov.nasa.jpl.aerie.scheduler.goals.CoexistenceGoal; import gov.nasa.jpl.aerie.scheduler.model.*; @@ -35,7 +35,7 @@ void setUp(){ @Test public void testFieldAnnotation(){ - final var fixedDurationActivityTemplate = new ActivityCreationTemplate.Builder() + final var fixedDurationActivityTemplate = new ActivityExpression.Builder() .ofType(problem.getActivityType("BananaNap")) .withTimingPrecision(Duration.of(500, Duration.MILLISECOND)) .build(); @@ -65,7 +65,7 @@ public void testFieldAnnotation(){ @Test public void testMethodAnnotation(){ - final var fixedDurationActivityTemplate = new ActivityCreationTemplate.Builder() + final var fixedDurationActivityTemplate = new ActivityExpression.Builder() .ofType(problem.getActivityType("RipenBanana")) .withTimingPrecision(Duration.of(500, Duration.MILLISECOND)) .build(); diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/ParametricDurationTest.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/ParametricDurationTest.java index b05d3175ac..028e1a7229 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/ParametricDurationTest.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/ParametricDurationTest.java @@ -7,8 +7,7 @@ import gov.nasa.jpl.aerie.merlin.driver.MissionModel; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; -import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeAnchor; +import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeExpression; import gov.nasa.jpl.aerie.scheduler.goals.CoexistenceGoal; import gov.nasa.jpl.aerie.scheduler.model.PlanningHorizon; @@ -38,7 +37,7 @@ void setUp(){ @Test public void testStartConstraint() { - final var parameterizedDurationActivityTemplate = new ActivityCreationTemplate.Builder() + final var parameterizedDurationActivityTemplate = new ActivityExpression.Builder() .ofType(problem.getActivityType("DownloadBanana")) .withArgument("connection", SerializedValue.of("DietaryFiberOptic")) .withTimingPrecision(Duration.of(500, Duration.MILLISECOND)) @@ -68,7 +67,7 @@ public void testStartConstraint() { @Test public void testEndConstraint() { - final var parameterizedDurationActivityTemplate = new ActivityCreationTemplate.Builder() + final var parameterizedDurationActivityTemplate = new ActivityExpression.Builder() .ofType(problem.getActivityType("DownloadBanana")) .withArgument("connection", SerializedValue.of("FiberOptic")) .withTimingPrecision(Duration.of(500, Duration.MILLISECOND)) diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/PrioritySolverTest.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/PrioritySolverTest.java index 9f155f4163..acca70bbaa 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/PrioritySolverTest.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/PrioritySolverTest.java @@ -7,7 +7,6 @@ import gov.nasa.jpl.aerie.constraints.tree.WindowsWrapperExpression; import gov.nasa.jpl.aerie.merlin.driver.MissionModel; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeAnchor; import gov.nasa.jpl.aerie.scheduler.goals.CardinalityGoal; @@ -192,9 +191,9 @@ public void getNextSolution_recurrenceGoalWorks() { .startingAt(t0) .endingAt(t2hr.plus(Duration.of(10, Duration.MINUTE))) .repeatingEvery(d1hr) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(problem.getActivityType("ControllableDurationActivity")) - .duration(d1min) + .durationIn(d1min) .build()) .withinPlanHorizon(h) .build(); @@ -227,9 +226,9 @@ public void getNextSolution_coexistenceGoalOnActivityWorks() { .forEach(new ActivityExpression.Builder() .ofType(actTypeA) .build()) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypeB) - .duration(d1min) + .durationIn(d1min) .build()) .startsAt(TimeAnchor.START) .aliasForAnchors("Bond. James Bond") @@ -273,9 +272,9 @@ public void getNextSolution_coexistenceGoalOnActivityWorks_withInitialSimResults .forEach(new ActivityExpression.Builder() .ofType(actTypeA) .build()) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypeB) - .duration(d1min) + .durationIn(d1min) .build()) .startsAt(TimeAnchor.START) .aliasForAnchors("Bond. James Bond") @@ -312,9 +311,9 @@ public void testCardGoalWithApplyWhen(){ CardinalityGoal cardGoal = new CardinalityGoal.Builder() .duration(Interval.between(Duration.of(2, Duration.SECONDS), Duration.of(65, Duration.HOUR))) .occurences(new Range<>(1, 3)) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(problem.getActivityType("ControllableDurationActivity")) - .duration(d1min) + .durationIn(d1min) .build()) .named("TestCardGoal") .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/SimulationFacadeTest.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/SimulationFacadeTest.java index 3dc318b97c..500334fa79 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/SimulationFacadeTest.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/SimulationFacadeTest.java @@ -20,7 +20,6 @@ import gov.nasa.jpl.aerie.merlin.driver.MissionModel; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeAnchor; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeExpressionConstant; @@ -153,7 +152,7 @@ public void associationToExistingSatisfyingActivity(){ final var goal = new CoexistenceGoal.Builder() .forEach(ActivityExpression.ofType(actTypePeel)) .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(horizon.getHor(), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder(). + .thereExistsOne(new ActivityExpression.Builder(). ofType(actTypeBite) .withArgument("biteSize", SerializedValue.of(0.1)) .build()) @@ -283,7 +282,7 @@ public void testCoexistenceGoalWithResourceConstraint() { CoexistenceGoal cg = new CoexistenceGoal.Builder() .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(horizon.getHor(), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypePeel) .withArgument("peelDirection", SerializedValue.of("fromStem")) .build()) 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..1a38f7d97a 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 @@ -25,7 +25,6 @@ import gov.nasa.jpl.aerie.constraints.tree.WindowsWrapperExpression; import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; import gov.nasa.jpl.aerie.scheduler.constraints.TimeRangeExpression; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeAnchor; import gov.nasa.jpl.aerie.scheduler.goals.CardinalityGoal; @@ -69,8 +68,8 @@ public void testRecurrenceCutoff1() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(1, Duration.SECONDS), Duration.of(12, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -105,8 +104,8 @@ public void testRecurrenceCutoff2() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(1, Duration.SECONDS), Duration.of(17, Duration.SECONDS)), true))) //add colorful tests that make use of windows capability - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -141,8 +140,8 @@ public void testRecurrenceShorterWindow() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(1, Duration.SECONDS), Duration.of(19, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -177,8 +176,8 @@ public void testRecurrenceLongerWindow() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(1, Duration.SECONDS), Duration.of(21, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -226,8 +225,8 @@ public void testRecurrenceBabyWindow() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(1, Duration.SECONDS), Duration.of(2, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(1, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(1, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -272,8 +271,8 @@ public void testRecurrenceWindows() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -318,8 +317,8 @@ public void testRecurrenceWindowsCutoffMidInterval() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -366,8 +365,8 @@ public void testRecurrenceWindowsGlobalCheck() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(3, Duration.SECONDS)) @@ -414,8 +413,8 @@ public void testRecurrenceWindowsCutoffMidActivity() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -450,7 +449,7 @@ public void testRecurrenceCutoffUncontrollable() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(1, Duration.SECONDS), Duration.of(12, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -493,9 +492,9 @@ public void testCardinality() { CardinalityGoal goal = new CardinalityGoal.Builder() .duration(Interval.between(Duration.of(16, Duration.SECONDS), Duration.of(19, Duration.SECONDS))) .occurences(new Range<>(3, 10)) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(problem.getActivityType("ControllableDurationActivity")) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .named("TestCardGoal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(period, true))) @@ -540,9 +539,9 @@ public void testCardinalityWindows() { CardinalityGoal goal = new CardinalityGoal.Builder() .duration(Interval.between(Duration.of(16, Duration.SECONDS), Duration.of(19, Duration.SECONDS))) .occurences(new Range<>(3, 10)) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(problem.getActivityType("ControllableDurationActivity")) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .named("TestCardGoal") .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) @@ -591,9 +590,9 @@ public void testCardinalityWindowsCutoffMidActivity() { CardinalityGoal goal = new CardinalityGoal.Builder() .duration(Interval.between(Duration.of(16, Duration.SECONDS), Duration.of(19, Duration.SECONDS))) .occurences(new Range<>(3, 10)) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(problem.getActivityType("ControllableDurationActivity")) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .named("TestCardGoal") .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) @@ -639,7 +638,7 @@ public void testCardinalityUncontrollable() { //ruled unpredictable for now CardinalityGoal goal = new CardinalityGoal.Builder() .duration(Interval.between(Duration.of(16, Duration.SECONDS), Duration.of(19, Duration.SECONDS))) .occurences(new Range<>(3, 10)) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(activityType) .build()) .named("TestCardGoal") @@ -691,7 +690,7 @@ public void testCoexistenceWindowCutoff() { problem.setInitialPlan(partialPlan); //want to create another activity for each of the already present activities // foreach with activityexpression - ActivityExpression framework = new ActivityCreationTemplate.Builder() + ActivityExpression framework = new ActivityExpression.Builder() .ofType(actTypeA) .build(); @@ -699,9 +698,9 @@ public void testCoexistenceWindowCutoff() { CoexistenceGoal goal = new CoexistenceGoal.Builder() .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(period, true))) .forEach(framework) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypeA) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .startsAt(TimeAnchor.START) .aliasForAnchors("Bond. James Bond") @@ -746,7 +745,7 @@ public void testCoexistenceJustFits() { problem.setInitialPlan(partialPlan); //want to create another activity for each of the already present activities // foreach with activityexpression - ActivityExpression framework = new ActivityCreationTemplate.Builder() + ActivityExpression framework = new ActivityExpression.Builder() .ofType(actTypeA) .build(); @@ -754,9 +753,9 @@ public void testCoexistenceJustFits() { CoexistenceGoal goal = new CoexistenceGoal.Builder() .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(period, true))) .forEach(framework) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypeA) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .startsAt(TimeAnchor.START) .aliasForAnchors("Bond. James Bond") @@ -808,7 +807,7 @@ public void testCoexistenceUncontrollableCutoff() { //ruled unpredictable for no problem.setInitialPlan(partialPlan); //want to create another activity for each of the already present activities // foreach with activityexpression - ActivityExpression framework = new ActivityCreationTemplate.Builder() + ActivityExpression framework = new ActivityExpression.Builder() .ofType(actTypeA) .build(); @@ -817,7 +816,7 @@ public void testCoexistenceUncontrollableCutoff() { //ruled unpredictable for no CoexistenceGoal goal = new CoexistenceGoal.Builder() .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(period, true))) .forEach(framework) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypeB) .build()) .startsAt(TimeAnchor.START) @@ -874,7 +873,7 @@ public void testCoexistenceWindows() { //want to create another activity for each of the already present activities // foreach with activityexpression - ActivityExpression framework = new ActivityCreationTemplate.Builder() + ActivityExpression framework = new ActivityExpression.Builder() .ofType(actTypeA) .build(); @@ -882,9 +881,9 @@ public void testCoexistenceWindows() { CoexistenceGoal goal = new CoexistenceGoal.Builder() .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) .forEach(framework) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypeA) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .startsAt(TimeAnchor.START) .aliasForAnchors("Bond. James Bond") @@ -945,7 +944,7 @@ public void testCoexistenceWindowsCutoffMidActivity() { //want to create another activity for each of the already present activities // foreach with activityexpression final var actTypeB = problem.getActivityType("OtherControllableDurationActivity"); - ActivityExpression framework = new ActivityCreationTemplate.Builder() + ActivityExpression framework = new ActivityExpression.Builder() .ofType(actTypeA) .build(); @@ -953,9 +952,9 @@ public void testCoexistenceWindowsCutoffMidActivity() { CoexistenceGoal goal = new CoexistenceGoal.Builder() .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) .forEach(framework) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypeB) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .startsAt(TimeAnchor.START) .aliasForAnchors("Bond. James Bond") @@ -1017,7 +1016,7 @@ public void testCoexistenceWindowsBisect() { //bad, should fail completely. wort //want to create another activity for each of the already present activities // foreach with activityexpression - ActivityExpression framework = new ActivityCreationTemplate.Builder() + ActivityExpression framework = new ActivityExpression.Builder() .ofType(actTypeA) .build(); @@ -1025,9 +1024,9 @@ public void testCoexistenceWindowsBisect() { //bad, should fail completely. wort CoexistenceGoal goal = new CoexistenceGoal.Builder() .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) .forEach(framework) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypeA) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .startsAt(TimeAnchor.START) .aliasForAnchors("Bond. James Bond") @@ -1084,7 +1083,7 @@ public void testCoexistenceWindowsBisect2() { //corrected. Bisection does work s //want to create another activity for each of the already present activities // foreach with activityexpression - ActivityExpression framework = new ActivityCreationTemplate.Builder() + ActivityExpression framework = new ActivityExpression.Builder() .ofType(actTypeA) .build(); @@ -1092,9 +1091,9 @@ public void testCoexistenceWindowsBisect2() { //corrected. Bisection does work s CoexistenceGoal goal = new CoexistenceGoal.Builder() .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) .forEach(framework) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypeA) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .startsAt(TimeAnchor.START) .aliasForAnchors("Bond. James Bond") @@ -1143,7 +1142,7 @@ public void testCoexistenceUncontrollableJustFits() { problem.setInitialPlan(partialPlan); //want to create another activity for each of the already present activities // foreach with activityexpression - ActivityExpression framework = new ActivityCreationTemplate.Builder() + ActivityExpression framework = new ActivityExpression.Builder() .ofType(actTypeA) .build(); @@ -1152,7 +1151,7 @@ public void testCoexistenceUncontrollableJustFits() { CoexistenceGoal goal = new CoexistenceGoal.Builder() .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(period, true))) .forEach(framework) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypeB) .build()) .startsAt(TimeAnchor.START) @@ -1205,7 +1204,7 @@ public void testCoexistenceExternalResource() { CoexistenceGoal goal = new CoexistenceGoal.Builder() .forAllTimeIn(new WindowsWrapperExpression(new Windows(period, true).assignGaps(new Windows(Interval.FOREVER, false)))) .forEach(new SpansFromWindows(new AssignGaps<>(cond, new WindowsWrapperExpression(new Windows(Interval.FOREVER, false))))) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(actTypeB) .withArgument("duration", profEx) .build()) @@ -1260,9 +1259,9 @@ public void changingForAllTimeIn() { CardinalityGoal whenActivitiesGreaterThan2 = new CardinalityGoal.Builder() .duration(Interval.between(Duration.of(16, Duration.SECONDS), Duration.of(19, Duration.SECONDS))) .occurences(new Range<>(3, 10)) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(activityTypeDependent) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .named("TestCardGoal") .forAllTimeIn(gte) @@ -1276,9 +1275,9 @@ public void changingForAllTimeIn() { RecurrenceGoal addRecurringActivityModifyingResource = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor.getHor(), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(activityTypeIndependent) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) .withinPlanHorizon(hor) @@ -1338,9 +1337,9 @@ public void changingForAllTimeInCutoff() { CardinalityGoal whenActivitiesGreaterThan2 = new CardinalityGoal.Builder() .duration(Interval.between(Duration.of(16, Duration.SECONDS), Duration.of(19, Duration.SECONDS))) .occurences(new Range<>(3, 10)) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(activityTypeDependent) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .named("TestCardGoal") .forAllTimeIn(gte) @@ -1354,9 +1353,9 @@ public void changingForAllTimeInCutoff() { RecurrenceGoal addRecurringActivityModifyingResource = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor.getHor(), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(activityTypeIndependent) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) .withinPlanHorizon(hor) @@ -1423,9 +1422,9 @@ public void changingForAllTimeInAlternativeCutoff() { CardinalityGoal whenActivitiesGreaterThan2 = new CardinalityGoal.Builder() .duration(Interval.between(Duration.of(16, Duration.SECONDS), Duration.of(19, Duration.SECONDS))) .occurences(new Range<>(3, 10)) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(activityTypeDependent) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .named("TestCardGoal") .forAllTimeIn(gte) @@ -1439,9 +1438,9 @@ public void changingForAllTimeInAlternativeCutoff() { RecurrenceGoal addRecurringActivityModifyingResource = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(hor.getHor(), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(activityTypeIndependent) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) .withinPlanHorizon(hor) diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestCardinalityGoal.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestCardinalityGoal.java index b59a8be240..cf841e9419 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestCardinalityGoal.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestCardinalityGoal.java @@ -4,7 +4,7 @@ import gov.nasa.jpl.aerie.constraints.time.Windows; import gov.nasa.jpl.aerie.constraints.tree.WindowsWrapperExpression; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; +import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.goals.CardinalityGoal; import gov.nasa.jpl.aerie.scheduler.goals.ChildCustody; import gov.nasa.jpl.aerie.scheduler.model.SchedulingActivityDirective; @@ -35,9 +35,9 @@ public void testone() { CardinalityGoal goal = new CardinalityGoal.Builder() .duration(Interval.between(Duration.of(12, Duration.SECONDS), Duration.of(15, Duration.SECONDS))) .occurences(new Range<>(3, 10)) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(problem.getActivityType("ControllableDurationActivity")) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .named("TestCardGoal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(period, true))) diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestRecurrenceGoal.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestRecurrenceGoal.java index 0b7bf5da13..4ee3516746 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestRecurrenceGoal.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestRecurrenceGoal.java @@ -4,7 +4,7 @@ import gov.nasa.jpl.aerie.constraints.time.Windows; import gov.nasa.jpl.aerie.constraints.tree.WindowsWrapperExpression; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; +import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.goals.RecurrenceGoal; import gov.nasa.jpl.aerie.scheduler.model.PlanningHorizon; import gov.nasa.jpl.aerie.scheduler.model.Problem; @@ -31,8 +31,8 @@ public void testRecurrence() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(1, Duration.SECONDS), Duration.of(20, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -68,8 +68,8 @@ public void testRecurrenceNegative() { .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(1, Duration.SECONDS), Duration.of(20, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(-1, Duration.SECONDS)) diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestRecurrenceGoalExtended.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestRecurrenceGoalExtended.java index dbdf654458..7dfdcc37bb 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestRecurrenceGoalExtended.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/TestRecurrenceGoalExtended.java @@ -4,7 +4,7 @@ import gov.nasa.jpl.aerie.constraints.time.Windows; import gov.nasa.jpl.aerie.constraints.tree.WindowsWrapperExpression; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; +import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.goals.RecurrenceGoal; import gov.nasa.jpl.aerie.scheduler.model.PlanningHorizon; import gov.nasa.jpl.aerie.scheduler.model.Problem; @@ -34,8 +34,8 @@ public void testRecurrence() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(1, Duration.SECONDS), Duration.of(20, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -69,8 +69,8 @@ public void testRecurrenceSecondGoalOutOfWindowAndPlanHorizon() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(1, Duration.SECONDS), Duration.of(20, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(18, Duration.SECONDS)) @@ -101,8 +101,8 @@ public void testRecurrenceRepeatIntervalLargerThanGoalWindow() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(1, Duration.SECONDS), Duration.of(20, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(20, Duration.SECONDS)) @@ -138,8 +138,8 @@ public void testGoalWindowLargerThanPlanHorizon() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -172,8 +172,8 @@ public void testGoalDurationLargerGoalWindow() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(0, Duration.SECONDS), Duration.of(20, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(25, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(25, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(30, Duration.SECONDS)) @@ -205,8 +205,8 @@ public void testGoalDurationLargerRepeatInterval() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(0, Duration.SECONDS), Duration.of(20, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(10, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(10, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -238,8 +238,8 @@ public void testAddActivityNonEmptyPlan() { RecurrenceGoal goal = new RecurrenceGoal.Builder() .named("Test recurrence goal") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(0, Duration.SECONDS), Duration.of(20, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) @@ -257,8 +257,8 @@ public void testAddActivityNonEmptyPlan() { RecurrenceGoal goal2 = new RecurrenceGoal.Builder() .named("Test recurrence goal 2") .forAllTimeIn(new WindowsWrapperExpression(new Windows(false).set(Interval.betweenClosedOpen(Duration.of(2, Duration.SECONDS), Duration.of(20, Duration.SECONDS)), true))) - .thereExistsOne(new ActivityCreationTemplate.Builder() - .duration(Duration.of(2, Duration.SECONDS)) + .thereExistsOne(new ActivityExpression.Builder() + .durationIn(Duration.of(2, Duration.SECONDS)) .ofType(activityType) .build()) .repeatingEvery(Duration.of(5, Duration.SECONDS)) 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 da1b2a39d0..2298e70fba 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 @@ -5,7 +5,6 @@ import gov.nasa.jpl.aerie.constraints.time.Windows; import gov.nasa.jpl.aerie.constraints.tree.WindowsWrapperExpression; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeAnchor; import gov.nasa.jpl.aerie.scheduler.goals.CardinalityGoal; @@ -64,7 +63,7 @@ public CoexistenceGoal BForEachAGoal(ActivityType A, ActivityType B){ .forEach(new ActivityExpression.Builder() .ofType(A) .build()) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(B) .build()) .startsAt(TimeAnchor.START) @@ -236,9 +235,9 @@ public void testCardinalityBacktrack() { CardinalityGoal unsatisfiableGoal = new CardinalityGoal.Builder() .duration(Interval.between(Duration.of(16, Duration.SECONDS), Duration.of(21, Duration.SECONDS))) .occurences(new Range<>(10, 10)) - .thereExistsOne(new ActivityCreationTemplate.Builder() + .thereExistsOne(new ActivityExpression.Builder() .ofType(problem.getActivityType("ControllableDurationActivity")) - .duration(Duration.of(2, Duration.SECONDS)) + .durationIn(Duration.of(2, Duration.SECONDS)) .build()) .named("TestCardGoal") .forAllTimeIn(new WindowsWrapperExpression(goalWindow)) diff --git a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/UncontrollableDurationTest.java b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/UncontrollableDurationTest.java index 5db8f2a8f3..4c1202bbc5 100644 --- a/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/UncontrollableDurationTest.java +++ b/scheduler-driver/src/test/java/gov/nasa/jpl/aerie/scheduler/UncontrollableDurationTest.java @@ -6,7 +6,6 @@ import gov.nasa.jpl.aerie.merlin.driver.MissionModel; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeAnchor; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeExpression; @@ -50,7 +49,7 @@ private PlanInMemory makeEmptyPlan() { public void testNonLinear(){ //duration should be 300 seconds trapezoidal - final var solarPanelActivityTrapezoidal = new ActivityCreationTemplate.Builder() + final var solarPanelActivityTrapezoidal = new ActivityExpression.Builder() .ofType(problem.getActivityType("SolarPanelNonLinear")) .withTimingPrecision(Duration.of(500, Duration.MILLISECOND)) .withArgument("theta_turn", SerializedValue.of(2.)) @@ -59,7 +58,7 @@ public void testNonLinear(){ .build(); //turn should be 89.44 secs and triangle shape - final var solarPanelActivityTriangle = new ActivityCreationTemplate.Builder() + final var solarPanelActivityTriangle = new ActivityExpression.Builder() .ofType(problem.getActivityType("SolarPanelNonLinear")) .withTimingPrecision(Duration.of(500, Duration.MILLISECOND)) .withArgument("theta_turn", SerializedValue.of(0.2)) @@ -102,7 +101,7 @@ public void testNonLinear(){ @Test public void testTimeDependent(){ - final var solarPanelActivityTrapezoidal = new ActivityCreationTemplate.Builder() + final var solarPanelActivityTrapezoidal = new ActivityExpression.Builder() .ofType(problem.getActivityType("SolarPanelNonLinearTimeDependent")) .withTimingPrecision(Duration.of(500, Duration.MILLISECOND)) .withArgument("command", SerializedValue.of(1.)) @@ -110,7 +109,7 @@ public void testTimeDependent(){ .withArgument("omega_max", SerializedValue.of(0.01)) .build(); - final var solarPanelActivityTriangle = new ActivityCreationTemplate.Builder() + final var solarPanelActivityTriangle = new ActivityExpression.Builder() .ofType(problem.getActivityType("SolarPanelNonLinearTimeDependent")) .withTimingPrecision(Duration.of(500, Duration.MILLISECOND)) .withArgument("command", SerializedValue.of(0.5)) @@ -158,7 +157,7 @@ public void testBug(){ Duration.of(1, Duration.MICROSECONDS), Duration.of(3, Duration.MICROSECONDS), null, true); - final var zeroDurationUncontrollableActivity = new ActivityCreationTemplate.Builder() + final var zeroDurationUncontrollableActivity = new ActivityExpression.Builder() .ofType(problem.getActivityType("ZeroDurationUncontrollableActivity")) .withTimingPrecision(Duration.of(1, Duration.MICROSECONDS)) .build(); @@ -192,7 +191,7 @@ public void testBug(){ @Test public void testScheduleExceptionThrowingTask(){ - final var zeroDurationUncontrollableActivity = new ActivityCreationTemplate.Builder() + final var zeroDurationUncontrollableActivity = new ActivityExpression.Builder() .ofType(problem.getActivityType("LateRiser")) .withTimingPrecision(Duration.of(1, Duration.MICROSECONDS)) .build(); 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..3a152aeec3 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 @@ -11,7 +11,6 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; import gov.nasa.jpl.aerie.merlin.protocol.types.DurationType; import gov.nasa.jpl.aerie.scheduler.Range; -import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityCreationTemplate; import gov.nasa.jpl.aerie.scheduler.constraints.activities.ActivityExpression; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeExpressionBetween; import gov.nasa.jpl.aerie.scheduler.constraints.timeexpressions.TimeExpressionRelativeFixed; @@ -195,17 +194,17 @@ private static Expression spansOfConstraintExpression( } } - private static ActivityCreationTemplate makeActivityTemplate( + private static ActivityExpression makeActivityTemplate( final SchedulingDSL.ActivityTemplate activityTemplate, final Function lookupActivityType) { - var builder = new ActivityCreationTemplate.Builder(); + var builder = new ActivityExpression.Builder(); final var type = lookupActivityType.apply(activityTemplate.activityType()); if(type.getDurationType() instanceof DurationType.Controllable durationType){ //detect duration parameter if(activityTemplate.arguments().fields().containsKey(durationType.parameterName())){ final var argument = activityTemplate.arguments().fields().get(durationType.parameterName()); if(argument != null){ - builder.duration(argument.expression); + builder.durationIn(argument.expression); activityTemplate.arguments().fields().remove(durationType.parameterName()); } else { //nothing, other cases will be handled by below section