Skip to content

Commit

Permalink
finish all examples minus Kotlin
Browse files Browse the repository at this point in the history
  • Loading branch information
psubram3 committed Oct 25, 2024
1 parent f3bd59c commit b207188
Showing 1 changed file with 198 additions and 3 deletions.
201 changes: 198 additions & 3 deletions docs/scheduling-and-constraints/procedural/scheduling/examples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,208 @@ public record CardinalityGoal(int minOccurrences) implements Goal {
</TabItem>
</Tabs>

## 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:

<Tabs groupId="lang">
<TabItem value="kt" label="Kotlin">

```kt
NOT WRITTEN YET
```

</TabItem>
<TabItem value="java" label="Java">

```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();
}
}
```

</TabItem>
</Tabs>

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:

<Tabs groupId="lang">
<TabItem value="kt" label="Kotlin">

```kt
NOT WRITTEN YET
```

</TabItem>
<TabItem value="java" label="Java">

```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))
);
}

<!--TODO!!!-->
plan.commit();
}
}
```

</TabItem>
</Tabs>

## 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]:

<Tabs groupId="lang">
<TabItem value="kt" label="Kotlin">

```kt
NOT WRITTEN YET
```

</TabItem>
<TabItem value="java" label="Java">

```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();
}
}
```

</TabItem>
</Tabs>


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:

<Tabs groupId="lang">
<TabItem value="kt" label="Kotlin">

```kt
NOT WRITTEN YET
```

</TabItem>
<TabItem value="java" label="Java">

```java
@SchedulingProcedure
public record ResourceCoexistenceGoal() implements Goal {

// grab the segment that includes start
private Segment<String> getSegment(Duration start, List<Segment<String>> 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();
}
}
```

</TabItem>
</Tabs>

## Scheduling Based off of External Events

Expand Down

0 comments on commit b207188

Please sign in to comment.