Skip to content

Commit

Permalink
Add tutorial for creating external event scheduling goal
Browse files Browse the repository at this point in the history
  • Loading branch information
JosephVolosin committed Dec 9, 2024
1 parent c6e9a79 commit 499be56
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 23 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import schedulingGoalUpload from './assets/scheduling_goal_upload.png';
import schedulingGoalModal from './assets/scheduling_goal_modal.png';
import schedulingGoalResult from './assets/scheduling_goal_result.png';

# Creating a Scheduling Goal with External Events
Now that we have a plan with a Derivation Group associated to it, we can create & run procedural scheduling goals that make use of External Events!

:::info Note

This tutorial page will talk specifically about using **External Events** in procedural scheduling. A more in-depth guide to procedural scheduling can be found [here](../../../scheduling-and-constraints/procedural/introduction/).

:::

:::info Note

The following sections is a partial walk-through of creating a procedural goal with some of the general goal setup removed. For reference, the full goal as it should be written is included in the [Full Example Goal](#full-example-goal) section.

:::

## Creating a Scheduling Goal

As a pre-requesite to creating a scheduling goal, follow the `Create a project from the template` steps found in the [procedural scheduling documentation](../../../scheduling-and-constraints/procedural/introduction/).

### Example: Scheduling an activity for `SampleTypeA`
One simple use case for External Events in scheduling goals is tie-ing an activity instance to whenever an External Event occurs. In this example we'll create a goal that creates a new activity for each occurrence of a `SampleTypeA`-typed External Event in the plan.

With the project initialized, create a new file within `scheduling/src/main/java/scheduling/procedures/` called `DemoSchedulingGoal.java`.

External Events can be collected within the scheduling goal by creating `EventQuery`s, and using `plan.events(yourQueryHere).collect()`. To grab all the External Events of type `SampleTypeA`, we can write the following `EventQuery`:

```java
EventQuery sampleTypeAEvents = new EventQuery(null, List.of("Sample Type A"), null);
```

:::info Note on nulls

The previous `EventQuery` only includes a value for the middle argument, `eventTypes`. A query can also be constructed by passing `Derivation Group(s)` to the first argument of the `EventQuery`, or by passing a list of `Sources` gathered by a `SourceQuery` to the last argument of the `EventQuery`.

:::

To retrieve the External Events associated with this `EventQuery`, we can use the following line:

```java
var exampleEvents = plan.events(sampleTypeAEvents).collect();
```

Prior to planning our new activity instances, we can simulate the plan and take note of all the existing activity instances:

```java
final var simResults = plan.simulate();
final var existingSpans = simResults.instances();
```

Next, we can iterate over all of our collected events and plan activities for them:

```java
for (var currentEvent : exampleEvents) {
if (!areThereSpansForType(existingSpans, currentEvent.getInterval().start, newActivityType)) {
final var newActivityName = currentEvent.key + " Activity";
final var currentEventExampleAttribute = currentEvent.attributes.get("EventExampleAttribute").asInt().get();
plan.create(
new NewDirective(
new AnyDirective(Map.of(
"temperature", SerializedValue.of(123.1),
"tbSugar", SerializedValue.of(currentEventExampleAttribute),
"glutenFree", SerializedValue.of(false)
)),
newActivityName,
newActivityType,
new DirectiveStart.Absolute(currentEvent.getInterval().start)
)
);
}
}
```

And finally, add a `plan.commit();` after the loop to finalize your goal!

If we compile & upload this goal, we can associate it with the plan we previously created and once scheduling is run a `BakeBananaBread` activity will be planned at the start of each `SampleTypeA` External Event!

### Example: Scheduling an activity for `SampleTypeA` using attributes

Building on the above example, you may want to constrain your goal to only plan activity instances on the `SampleTypeA` External Events that have their `EventExampleAttribute` attribute set to `1`. The previous example gathers the `EventExampleAttribute` for use with the `tbSugar` arugment on the `BakeBananaBread` activity but instead we could use it in the `for` loop to filter the External Events we're using for planning:

```java
for (var currentEvent : exampleEvents) {
if (!areThereSpansForType(existingSpans, currentEvent.getInterval().start, newActivityType)) {
final var currentEventExampleAttribute = currentEvent.attributes.get("EventExampleAttribute").asInt().get();
if (currentEventExampleAttribute == 1) {
final var newActivityName = currentEvent.key + " Activity";
plan.create(
new NewDirective(
new AnyDirective(Map.of(
"temperature", SerializedValue.of(123.1),
"tbSugar", SerializedValue.of(currentEventExampleAttribute),
"glutenFree", SerializedValue.of(false)
)),
newActivityName,
newActivityType,
new DirectiveStart.Absolute(currentEvent.getInterval().start)
)
);
}
}
}
```

### Full Example Goal

```java
package scheduling.procedures;

import gov.nasa.ammos.aerie.procedural.scheduling.Goal;
import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure;
import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan;
import gov.nasa.ammos.aerie.procedural.scheduling.plan.NewDirective;
import gov.nasa.ammos.aerie.procedural.timeline.collections.Instances;
import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective;
import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyInstance;
import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart;
import gov.nasa.ammos.aerie.procedural.timeline.plan.EventQuery;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue;

import java.util.List;
import java.util.Map;

@SchedulingProcedure
public record DemoSchedulingGoal() implements Goal {

boolean areThereSpansForType(Instances<AnyInstance> spans, Duration startTime, String activityType) {
final var filteredByType = spans.filter(false, it -> it.getType().equals(activityType));
final var filteredByTime = filteredByType.filter(false, it -> it.getStartTime().equals(startTime));
return !filteredByTime.collect().isEmpty();
}

@Override
public void run(EditablePlan plan) {
final var newActivityType = "BakeBananaBread"; // This can be any activity type in your model!
EventQuery sampleTypeAEvents = new EventQuery(null, List.of("SampleTypeA"), null);
var exampleEvents = plan.events(sampleTypeAEvents).collect();

final var simResults = plan.simulate();
final var existingSpans = simResults.instances();

for (var currentEvent : exampleEvents) {
if (!areThereSpansForType(existingSpans, currentEvent.getInterval().start, newActivityType)) {
var currentEventExampleAttribute = currentEvent.attributes.get("EventExampleAttribute").asInt().get();
if (currentEventExampleAttribute == 1) {
final var newActivityName = currentEvent.key + " Activity";
plan.create(
new NewDirective(
new AnyDirective(Map.of(
"temperature", SerializedValue.of(123.1),
"tbSugar", SerializedValue.of(currentEventExampleAttribute),
"glutenFree", SerializedValue.of(false)
)),
newActivityName,
newActivityType,
new DirectiveStart.Absolute(currentEvent.getInterval().start)
)
);
}
}
}
plan.commit();
}
}
```

## Uploading the Scheduling Goal
After the goal has been created and saved, it must be compiled following the steps in [Getting Started](../../../scheduling-and-constraints/procedural/getting-started/). The following commands should be run from the root of the project directory:

```bash
./gradlew :scheduling:compileJava
./gradlew :scheduling:buildAllProceduralSchedulingJars
```

Afterwards, a `DemoSchedulingGoal.jar` should be created in `$project/scheduling/build/libs/`!

In the Aerie UI, navigate back to the plan view for our previously created plan and swap the top-left drop-down menu to the `Scheduling Goals` tab. From there, click `Manage Goals` and then the `New` button to be directed to creating/uploading a new scheduling goal.

<figure>
<img alt="Opening the scheduling goal management modal" src={schedulingGoalModal} />
</figure>

In the creation form, enter a name (for example, `DemoSchedulingGoal`), swap the `EDSL/Jar File` button to `Jar File`, and then click `Choose File` and select the previously created `.jar` file. Your screen should show the following:

<figure>
<img alt="Successful scheduling goal upload" src={schedulingGoalUpload} />
</figure>

Back in the plan view, the goal can now be associated and run

<figure>
<img alt="Result from running scheduling goal" src={schedulingGoalResult} />
<figcaption>Two new activities should be created to satisfy the scheduling goal!</figcaption>
</figure>

## Additional Resources

More detail on the topics discussed in this tutorial can be found under [External Events](../../../planning/external-events)
45 changes: 22 additions & 23 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const sidebars = {
'tutorials/external-events/creating-an-external-source',
'tutorials/external-events/uploading-an-external-source',
'tutorials/external-events/associating-derivation-groups',
'tutorials/external-events/creating-a-scheduling-goal-with-external-events',
],
},
],
Expand Down Expand Up @@ -160,9 +161,9 @@ const sidebars = {
label: 'External Events',
link: {
id: 'planning/external-events/introduction',
type: 'doc'
type: 'doc',
},
items: ['planning/external-events/external-events-attributes']
items: ['planning/external-events/external-events-attributes'],
},
{
type: 'category',
Expand Down Expand Up @@ -197,7 +198,7 @@ const sidebars = {
label: 'Scheduling & Constraints',
link: {
id: 'scheduling-and-constraints/introduction',
type: 'doc'
type: 'doc',
},
items: [
'scheduling-and-constraints/management',
Expand All @@ -206,7 +207,7 @@ const sidebars = {
label: 'Procedural',
link: {
id: 'scheduling-and-constraints/procedural/introduction',
type: 'doc'
type: 'doc',
},
items: [
'scheduling-and-constraints/procedural/getting-started',
Expand All @@ -215,39 +216,39 @@ const sidebars = {
label: 'Timelines',
link: {
id: 'scheduling-and-constraints/procedural/timelines/introduction',
type: 'doc'
type: 'doc',
},
items: [
{
type: 'category',
label: "Basics",
label: 'Basics',
link: {
id: 'scheduling-and-constraints/procedural/timelines/basics/introduction',
type: 'doc'
type: 'doc',
},
items: [
'scheduling-and-constraints/procedural/timelines/basics/profiles',
'scheduling-and-constraints/procedural/timelines/basics/sampling-and-caching',
'scheduling-and-constraints/procedural/timelines/basics/activities',
'scheduling-and-constraints/procedural/timelines/basics/external-events',
'scheduling-and-constraints/procedural/timelines/basics/windows',
'scheduling-and-constraints/procedural/timelines/basics/common-operations'
]
'scheduling-and-constraints/procedural/timelines/basics/common-operations',
],
},
{
type: 'category',
label: "Advanced",
label: 'Advanced',
link: {
id: 'scheduling-and-constraints/procedural/timelines/advanced/introduction',
type: 'doc'
type: 'doc',
},
items: [
// 'scheduling-and-constraints/procedural/timelines/advanced/parallel-profiles',
// 'scheduling-and-constraints/procedural/timelines/advanced/custom-operations',
// 'scheduling-and-constraints/procedural/timelines/advanced/custom-timelines'
]
}
]
],
},
],
},
'scheduling-and-constraints/procedural/plan-and-sim-results',
'scheduling-and-constraints/procedural/constraints',
Expand All @@ -256,22 +257,20 @@ const sidebars = {
label: 'Scheduling',
link: {
id: 'scheduling-and-constraints/procedural/scheduling/introduction',
type: 'doc'
type: 'doc',
},
items: [
'scheduling-and-constraints/procedural/scheduling/examples'
]
items: ['scheduling-and-constraints/procedural/scheduling/examples'],
},
'scheduling-and-constraints/procedural/parameters-and-invocations',
// 'scheduling-and-constraints/procedural/running-externally'
]
],
},
{
type: 'category',
label: 'Declarative',
link: {
id: 'scheduling-and-constraints/declarative/introduction',
type: 'doc'
type: 'doc',
},
items: [
{
Expand Down Expand Up @@ -310,10 +309,10 @@ const sidebars = {
},
],
},
]
],
},
'scheduling-and-constraints/execution'
]
'scheduling-and-constraints/execution',
],
},
{
type: 'category',
Expand Down

0 comments on commit 499be56

Please sign in to comment.