The AND Goal aggregates several goals together and specifies that at least one of them must be satisfied.
The scheduler will try to satisfy each subgoal in the list. If a subgoal is only partially satisfied, the scheduler will not backtrack and will let the inserted activities in the plan. If all the subgoals are satisfied, the AND goal will appear satisfied. If one or several subgoals have not been satisfied, the AND goal will appear unsatisfied.
export default function myGoal() {
return Goal.CoexistenceGoal({
forEach: Real.Resource("/fruit").equal(4.0),
activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}),
endsAt: TimingConstraint.singleton(WindowProperty.END).plus(Temporal.Duration.from({ minutes: 5 }))
}).and(
Goal.CardinalityGoal({
activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}),
specification: { occurrence : 10 }
}))
}
The AND goal above has two subgoals. The coexistence goal will place activities of type PeelBanana
everytime the /fruit
resource is equal to 4. The second goal will place 10 occurrences of the same kind of activities PeelBanana
. The first subgoal will be evaluated first and will place a certain number of PeelBanana
activities in the plan. When the second goal will be evaluated, it will count already present PeelBanana
activities and insert the missing number. Imagine the first goals leads to inserting 2 activities. The second goal will then have to place 8 activities to be satisfied.
Rest
...others: Goal[]the list of goals
Restricts the windows on which a goal is applied
By default, a goal applies on the whole planning horizon. The Aerie scheduler provides support for restricting when a goal applies with the .applyWhen()
method in the Goal
class. This node allows users to provide a set of windows (Windows
, see documentation) which could be a time or a resource-based window.
The .applyWhen()
method, takes one argument: the windows (in the form of an expression) that the goal should apply over. What follows is an example that applies a daily recurrence goal only when a given resource is greater than 2. If the resource is less than two, then the goal is no longer applied.
export default function myGoal() {
return Goal.ActivityRecurrenceGoal({
activityTemplate: ActivityTemplates.GrowBanana({
quantity: 1,
growingDuration: Temporal.Duration.from({ hours: 1 })
}),
interval: Temporal.Duration.from({ hours: 2 })
}).applyWhen(Real.Resource("/fruit").greaterThan(2))
}
Recurrence interval: [++-++-++-]
Goal window: [+++++----]
Result: [++-------]
That, is, the second activity won't be scheduled as the goal window cuts off its recurrence interval.
Recurrence interval: [++-++-++-++-]
Goal window: [+++++--+++--]
Result: [++-----++---] //(the second one is applied independently of the first!)
a new goal applying on a restricted horizon
the windows on which this goal applies
The OR Goal aggregates several goals together and specifies that at least one of them must be satisfied.
The scheduler will try to satisfy each subgoal in the list until one is satisfied. If a subgoal is only partially satisfied, the scheduler will not backtrack and will let the inserted activities in the plan.
export default function myGoal() {
return Goal.CardinalityGoal({
activityTemplate: ActivityTemplates.GrowBanana({
quantity: 1,
growingDuration: Temporal.Duration.from({ hours: 1 })
}),
specification: { occurrence : 10 }
}).or(
Goal.ActivityRecurrenceGoal({
activityTemplate: ActivityTemplates.GrowBanana({
quantity: 1,
growingDuration: Temporal.Duration.from({ hours: 1 })
}),
interval: Temporal.Duration.from({ hours: 2 })
}))
}
If the plan has a 24-hour planning horizon, the OR goal above will try placing activities of the GrowBanana
type. The first subgoal will try placing 10 1-hour occurrences. If it fails to do so, because the planning horizon is maybe too short, it will then try to schedule 1 activity every 2 hours for the duration of the planning horizon.
It may fail to achieve both subgoals but as the scheduler does not backtrack for now, activities inserted by any of the subgoals are kept in the plan.
Rest
...others: Goal[]the list of goals
Specifies whether the scheduler should rollback or not if the goal is only partially satisfied. Rolling back is removing all inserted activities to satisfy the goal as well as removing associations to existing activities
boolean specifying the behavior of the scheduler in terms of rollback
Static
ActivityCreates an ActivityRecurrenceGoal
The Activity Recurrence Goal (sometimes referred to as a "frequency goal") specifies that a certain activity should occur repeatedly throughout the plan, at some given interval.
The separatedByAtMost constraint is treated as an upper bound - so if the activity occurs more frequently, that is not considered a failure.
The scheduler will find places in the plan where the given activity has not occurred within the given interval, and it will place an instance of that activity there.
Note: The interval is measured between the start times of two activity instances. Neither the duration, nor the end time of the activity are examined by this goal.
export default function myGoal() {
return Goal.ActivityRecurrenceGoal({
activityTemplate: ActivityTemplates.GrowBanana({
quantity: 1,
growingDuration: Temporal.Duration.from({ hours: 1 })
}),
separatedByAtMost: Temporal.Duration.from({ hours: 2 })
separatedByAtLeast: Temporal.Duration.from({ hours: 1 })
previousActivityStartedAt: Temporal.Duration.from({ hours: -1 })
})
}
If the plan contains intervals of more than 2 hours during which there is no activity of the desired type, this goal will insert an activity.
It will ensure that the activities are separated by at least 1 hour. The solver will consider that there is a fictional activity at time -1.
It follows that the first activity can be inserted in interval [-1, -1] + [1, 2] = [0, 1].
```typescript
export default function myGoal() {
return Goal.ActivityRecurrenceGoal({
activityTemplate: ActivityTemplates.GrowBanana({
quantity: 1,
growingDuration: Temporal.Duration.from({ hours: 1 })
}),
interval: Temporal.Duration.from({ hours: 2 })
})
}
The goal above will place a GrowBanana
activity every 2 hours exactly (without flexibility) if there is not already one with the exact same parameters. It is
equivalent to
export default function myGoal() {
return Goal.ActivityRecurrenceGoal({
activityTemplate: ActivityTemplates.GrowBanana({
quantity: 1,
growingDuration: Temporal.Duration.from({ hours: 1 })
}),
separatedByAtMost: Temporal.Duration.from({ hours: 2 })
separatedByAtLeast: Temporal.Duration.from({ hours: 2 })
previousActivityStartedAt: Temporal.Duration.from({ hours: -2 })
})
}
export default function myGoal() {
return Goal.ActivityRecurrenceGoal({
activityFinder: ActivityExpression.build(ActivityTypes.GrowBanana, {growingDuration: Temporal.Duration.from({hours : 1})})
activityTemplate: ActivityTemplates.GrowBanana({
quantity: 1,
growingDuration: Temporal.Duration.from({ hours: 1 })
}),
interval: Temporal.Duration.from({ hours: 2 })
})
}
Same behavior as above but in this last example, an activity finder is used to match against existing activities in the plan that would satisfy the goal. Here, any GrowBanana activity with a growingDuration
parameter equal to Temporal.Duration.from({hours : 1})
would match, disregarding the value of the other parameter quantity
.
@param opts an object containing the activity template and the interval at which the activities must be placed
Static
CardinalityThe Cardinality Goal specifies that a certain activity should occur in the plan either a certain number of times, or for a certain total duration.
occurrence
field, a duration
field, or both (see examples below).The duration and occurrence are treated as lower bounds - so if the activity occurs more times, or for a longer duration, that is not considered a failure, and the scheduler will not add any more activities.
The scheduler will identify whether it not the plan has enough occurrences or total duration of the given activity template. If not, it will add activities until satisfaction.
Setting a lower bound on the total duration:
export default function myGoal() {
return Goal.CardinalityGoal({
activityTemplate: ActivityTemplates.GrowBanana({
quantity: 1,
growingDuration: Temporal.Duration.from({ seconds: 1 }),
}),
specification: { duration: Temporal.Duration.from({ seconds: 10 }) }
})
}
Setting a lower bound on the number of occurrences:
export default function myGoal() {
return Goal.CardinalityGoal({
activityTemplate: ActivityTemplates.GrowBanana({
quantity: 1,
growingDuration: Temporal.Duration.from({ seconds: 1 }),
}),
specification: { occurrence: 10 }
})
}
Combining the two:
export default function myGoal() {
return Goal.CardinalityGoal({
activityTemplate: ActivityTemplates.GrowBanana({
quantity: 1,
growingDuration: Temporal.Duration.from({ seconds: 1 }),
}),
specification: { occurrence: 10, duration: Temporal.Duration.from({ seconds: 10 }) }
})
}
With an activity finder:
export default function myGoal() {
return Goal.CardinalityGoal({
activityTemplate: ActivityTemplates.GrowBanana({
quantity: 1,
growingDuration: Temporal.Duration.from({ seconds: 1 }),
}),
activityFinder: ActivityExpression.build(ActivityTypes.GrowBanana, {quantity: 1}),
specification: { occurrence: 10, duration: Temporal.Duration.from({ seconds: 10 }) }
})
}
In this last example, an activity finder is used to match against existing activities in the plan that would satisfy the goal. Here, any GrowBanana activity with a quantity
parameter equal to 1 would match, disregarding the value of the other parameter growingDuration
.
NOTE: In order to avoid placing multiple activities at the same start time, the Cardinality goal introduces an assumed mutual exclusion constraint - namely that new activities will not be allowed to overlap with existing activities.
an object containing the activity template and a CardinalityGoalArguments specification of what kind of cardinality is considered
Optional
activityStatic
CoexistenceCreates a coexistence goal. The coexistence goal places one activity for each passed window. The start and end time of each activity can be parametrized relatively to its coexisting window with temporal constraints.
The Coexistence Goal specifies that a certain activity should occur once for each occurrence of some condition.
Windows
, see documentation on how to produce such an expression) or a set of activities (ActivityExpression
) or an Interval or a Temporal.InstantforEach
. If activityFinder is not defined, used for both matching against existing activities and creating new ones.NOTE: Either the start or end of the activity must be constrained. This means that at least 1 of the 4 properties startsAt
, startsWithin
, endsAt
, endsWithin
must be given.
The scheduler will find places in the plan where the forEach
condition is true, find if an activity matching either the activity template or activity finder is present and if not, it will insert a new instance using the given activityTemplate
and temporal constraints.
export default () => Goal.CoexistenceGoal({
forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana),
activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}),
startsAt: TimingConstraint.singleton(WindowProperty.END).plus(Temporal.Duration.from({ minutes: 5 }))
})
Behavior: for each activity A of type GrowBanana
present in the plan when the goal is evaluated, place an activity of type PeelBanana
starting exactly at the end of A + 5 minutes.
export default () => Goal.CoexistenceGoal({
forEach: ActivityExpression.ofType(ActivityTypes.GrowBanana),
activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}),
startsWithin: TimingConstraint.range(WindowProperty.END, Operator.PLUS, Temporal.Duration.from({ minutes: 5 })),
endsWithin: TimingConstraint.range(WindowProperty.END, Operator.PLUS, Temporal.Duration.from({ minutes: 6 }))
})
Behavior: for each activity A of type GrowBanana
present in the plan when the goal is evaluated, place an activity of type PeelBanana
starting in the interval [end of A, end of A + 5 minutes] and ending in the interval [end of A, end of A + 6 minutes].
export default () => Goal.CoexistenceGoal({
forEach: Real.Resource("/fruit").equal(4.0),
activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}),
endsAt: TimingConstraint.singleton(WindowProperty.END).plus(Temporal.Duration.from({ minutes: 5 }))
})
Behavior: for each continuous period of time during which the /fruit
resource is equal to 4, place an activity of type PeelBanana
ending exactly at the end of A + 6 minutes. Note that the scheduler will allow a default timing error of 500 milliseconds for temporal constraints. This parameter will be configurable in an upcoming release.
KNOWN ISSUE: If the end is unconstrained while the activity has an uncontrollable duration, the scheduler may fail to place the activity. To work around this, add an endsWithin
constraint that encompasses your expectation for the duration of the activity - this will help the scheduler narrow the search space.
export default () => Goal.CoexistenceGoal({
forEach: Real.Resource("/fruit").equal(4.0),
activityTemplate: ActivityTemplates.PeelBanana({peelDirection: "fromStem"}),
activityFinder: ActivityExpression.ofType(ActivityTypes.PeelBanana)
endsAt: TimingConstraint.singleton(WindowProperty.END).plus(Temporal.Duration.from({ minutes: 5 }))
})
Behavior: Same as above but the activity finder is being used to match against any existing PeelBanana activity in the plan, disregarding the value of its parameters.
an object containing the activity template, a set of windows, and optionally temporal constraints.
Generated using TypeDoc
This class represents allows to represent and specify goals.