From b207188d2c66e5a8eac101f20cdbfc10a71bbf6b Mon Sep 17 00:00:00 2001 From: psubram3 Date: Fri, 25 Oct 2024 10:42:40 -0700 Subject: [PATCH] finish all examples minus Kotlin --- .../procedural/scheduling/examples.mdx | 201 +++++++++++++++++- 1 file changed, 198 insertions(+), 3 deletions(-) diff --git a/docs/scheduling-and-constraints/procedural/scheduling/examples.mdx b/docs/scheduling-and-constraints/procedural/scheduling/examples.mdx index 65ffeb1..ae6c5da 100644 --- a/docs/scheduling-and-constraints/procedural/scheduling/examples.mdx +++ b/docs/scheduling-and-constraints/procedural/scheduling/examples.mdx @@ -184,13 +184,208 @@ public record CardinalityGoal(int minOccurrences) implements Goal { -## Scheduling From Resources +## Scheduling From Resources (or External Profiles) Finally, we can talk about coexistence goals. These involve scheduling activities based off of windows where conditions are satisfied, such as the presence of certain activities, the presence of certain resources, or external events. We discuss how to schedule off of resource -condition satisfaction here, and discuss external events (which are functionally similar to activities) next. +condition satisfaction here, and discuss activities and external events (which are functionally similar to activities) next. + +The goal we seek to replicate is one provided in the description of [Coexistence Goal](../../declarative/scheduling/goals/#coexistence-goal). +Specifically, it was the following: + +```ts + 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 })), + }); +``` + +This goal very simply places an activity 5 minutes after the end of the window where `/fruit` is equal to `4.0`. +This can simply be replicated as follows: + + + + +```kt +NOT WRITTEN YET +``` + + + + +```java +@SchedulingProcedure +public record ResourceCoexistenceGoal() implements Goal { + @Override + public void run(@NotNull final EditablePlan plan) { + // access the resource + final var fruitResource = plan.resource("/fruit", Numbers.deserializer()); + for (final var segment: fruitResource.equalTo(4.0)) { + // place the activity off of the window that the resource access created + plan.create( + "PeelBanana", + new DirectiveStart.Absolute(segment.getInterval().start.plus(Duration.of(5, Duration.MINUTES))), + Map.of("peelDirection", SerializedValue.of("fromStem")) + ); + } + plan.commit(); + } +} +``` + + + + +Note that an implementation of a procedure that schedules based off of an external profile is exactly the same. +For example, with an external boolean profile `/my_boolean`, where we place `BiteBanana` instances wherever it holds true, we would have the following: + + + + +```kt +NOT WRITTEN YET +``` + + + + +```java +@SchedulingProcedure +public record ExternalProfileGoal() implements Goal { + @Override + public void run(@NotNull final EditablePlan plan) { + final var myBoolean = plan.resource("/my_boolean", Booleans.deserializer()); + for (final var interval: myBoolean.highlightTrue()) { + plan.create( + "BiteBanana", + new DirectiveStart.Absolute(interval.start), + Map.of("biteSize", SerializedValue.of(1)) + ); + } - + plan.commit(); + } +} +``` + + + + +## Scheduling From Activities + +Scheduling from activities is relatively straightforward. Replicating a basic example from [Coexistence Goal](../../declarative/scheduling/goals/#coexistence-goal) +where for each instance of `GrowBanana` we place a `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]: + + + + +```kt +NOT WRITTEN YET +``` + + + + +```java +@SchedulingProcedure +public record ActivityCoexistenceGoal() implements Goal { + @Override + public void run(@NotNull final EditablePlan plan) { + // first, grab all the "GrowBanana" activities + var activities = plan.directives().filterByType("GrowBanana"); + + for (final var a : activities) { + // to replicate the variance that might come with declarative scheduling, we use some randomization in these bounds to get a duration + // we need an activity that starts between a.end + [0,5] minutes + final var startMinutes = (int) (6 * Math.random()); + final var startOffset = Duration.of(startMinutes, Duration.MINUTES); + + // and we need an activity that end between [startOffset, a.end + 6] minutes + final var endMinutes = (int) ((6-startMinutes) * Math.random()) + startMinutes; + final var endOffset = Duration.of(endMinutes, Duration.MINUTES); + + // NOTE: we still need control/visibility of the placed activity's duration, preferably exposed as a parameter, so that we can set it + plan.create( + "PeelBanana", + new DirectiveStart.Absolute(a.getStartTime().plus(startOffset)), + Map.of( + "peelDirection", SerializedValue.of("fromStem"), + "growingDuration", BasicValueMappers.duration().serializeValue(endOffset.minus(startOffset)) + ) + ); + } + plan.commit(); + } +} +``` + + + + + +We can even replicate more complicated examples. If we want to, in our "template" for the activity that we will place, utilize +the value of a resource, we can also do this. Consider the following goal that places a `ChangeProducer` activity at each instance +of a `GrowBanana` activity, passing as a parameter the value of the `/producer` resource at the current moment: + + + + +```kt +NOT WRITTEN YET +``` + + + + +```java +@SchedulingProcedure +public record ResourceCoexistenceGoal() implements Goal { + + // grab the segment that includes start + private Segment getSegment(Duration start, List> segments) { + for (final var segment : segments) { + if (segment.getInterval().contains(start)) { + return segment; + } + } + return null; + } + + @Override + public void run(@NotNull final EditablePlan plan) { + // access the /producer resource, and collect it into a profile; do this only if + final var producerResource = plan.resource("/producer", Strings.deserializer()); + + // grab all the "GrowBanana" activities + var activities = plan.directives().filterByType("GrowBanana"); + + for (final var a : activities) { + // collect the resource at this time (it is lazily evaluated!) + var collected = producerResource.collect(); + + // get the segment when a occurs, and extract the value + String producerValue = getSegment(a.getInterval().start, collected).value; + + // create the activity, 5 minutes after this window + plan.create( + "ChangeProducer", + new DirectiveStart.Absolute(a.getStartTime().plus(a.getInterval().end.plus(Duration.of(5, Duration.MINUTES)))), + Map.of( + "producer", SerializedValue.of(producerValue) + ) + ); + } + + plan.commit(); + } +} +``` + + + ## Scheduling Based off of External Events